For developers

Point it at your backend — it evolves the prompts, not the model.

Declare your tunable params, hand it a labelled dataset, run one command and walk away. It generates candidates, scores them on your data, critiques the weak ones and regenerates until the numbers stop moving. It never edits your code, and it runs on your machine against your keys.

Where it runs

Built for one machine everyone had to reach.

It started on a single box. The data was Excel — some of it in the cloud, some of it sitting on people’s own laptops — and the whole team needed to work against the same instance. So it had to be networked; and once it was networked, it had to be locked. Everything below follows from that, which is why the security model is load-bearing rather than decorative.

Providers

OpenRouter, Groq, OpenAI and Anthropic.

Groq, OpenAI and OpenRouter share one OpenAI-compatible client behind a per-provider spec; Anthropic gets its own, because it does not speak the same wire format. Both satisfy one interface, so a provider swap is a config line. The provider is always named explicitly — there is no auto-detection and no env-var fallback, so a run can never quietly bill the wrong account.

Install & keys

Connectors

One client, adapted per backend.

The thing being optimized sits behind a single backend client parameterized by a connector — the wire adapter, the session, whether it runs over HTTP or in-process. Two ship today; third-party connectors register through a Python entry-point group, so adding one does not mean forking anything.

Backend integration

Deployment

Your terminal, or a box your team can reach.

The CLI runs as its own process on your machine and needs nothing else. The networked shape is the same install with a tunnel and a sign-in allowlist in front. app.promptpotter.com is not a different product — it is that deploy script pointed at our box instead of yours.

Deploy guide

Four layers, one command. The optimizer escalates only when it has to.

Production-grade prompts at round speed.

Learn more

Every round, L1 reads the prior winners and the critique log, then proposes a fresh population of candidate prompts. PoBB elimination (ε=0.05, n_min=4) culls the weak so spend tracks signal — no flat sweeps, no wasted queries.

round_0004/candidates.json
winner_id   "6a674185"
composite   0.94   // +0.31 vs origin
parent      "3c1d9020"  (round 3 winner)
eliminated  11 of 16  // PoBB ε=0.05, n_min=4
critique    "add explicit combinatorial count cue"

Don’t just tune the prompt — refine what the task even is.

When L1 plateaus across rounds, L2 fires once. It reads the stalled critique stream and rewrites task_context — never pipeline_params. The next L1 round inherits the new framing and goes again, often with a step-change in fitness.

// L2 fired after 3 stalled rounds on hard samples
task_context   "This dataset rewards step-by-step combinatorial
              enumeration. Candidates that skip the count step
              consistently miss multi-hop entries — make the
              counting explicit before the answer."

pipeline_params (unchanged — L2 never edits these)

When refinement isn’t enough, replan.

L3 fires only when both L1 and L2 have stalled. It scraps the plan and writes a new one — different sample budget, different scoring composite, different exploration policy. The new plan becomes the contract for round five and on.

round_0007/l3_plan.json
verdict      "replan"
budget       samples_per_round: 64 → 128
composite    accuracy + latency_penalty * 0.15
policy       "prefer wide exploration over fine sweep"
rationale    "L2's task_context edit didn't move composite
              past 0.71 — the scoring shape is wrong, not the prompt"

A recursive layer. The Potter improves its own meta-prompt.

Recursive optimization

  • Inner cycle exposed via the promptpotter connector
  • Outer Potter mutates the inner’s L1 / L2 / L3 templates
  • One command — no second runtime to deploy

Observability

  • Every round on disk in human-readable JSON
  • Live dashboard at /ui polls every two seconds
  • Full Langfuse trace for every LLM call

Cost & safety

  • Spend headline on the dashboard, never patience
  • Atomic ledger — Ctrl+C loses zero work
  • Multi-tenant identity, OIDC at the API boundary
Observability

Watch every call the optimizer makes.

A campaign spends your money while you are not looking at it. So every optimizer call, backend match and escalation check emits a structured trace — to your disk always, to Langfuse when you configure it.

Cloud tracing is off until you set the keys. When you set them the payloads go too — prompts, candidates, queries, ground truth, answers. That is what makes a trace worth having, and why switching it on is a decision about your data. The host is a plain setting: point it wherever you like, or leave it unset and nothing leaves the machine.

Shape of a trace

  • Campaign one trace
  • Round a span
  • L1 / L2 / L3 / critique call an observation
  • Backend match a span

Nested the way the run happened — a round that went sideways is a subtree you open, not timestamps you correlate.

What lands in it

Optimizer calls
prompt out, candidates back, token counts — per call, not per round
Backend matches
query, params, result, and any warnings the backend attached
Escalations
which rule fired, its priority, the reason, and the signals it read
Scores
origin, per round, per evaluator, hit-or-miss per query
Dataset
up to 100 pairs, linked back to the runs that scored them
Local, always An append-only log per run, on disk, no account. Pure mirror — nothing reads it back, so deleting it cannot corrupt a campaign.
Never fatal A dropped span is a debug line. An expired key or missing package warns at startup rather than killing an eight-hour run.
MLflow too A peer sink beside Langfuse, off by default.
Observability reference ↗
Security

Verifiable, not certified.

There is no SOC 2 report, no ISO 27001 certificate and no third-party penetration test. Saying otherwise would be the easiest lie on this site. What exists instead is every enforcement point written down, in a repository you can read — which is worth more than a badge if you intend to run this yourself, and worth checking before you do.

Sign-in

Google or GitHub OIDC, verified at the API boundary. No session means a 401 before any handler runs — there is no anonymous read path to forget about.

ADR-0002

Sessions

The cookie carries a random server-side id and nothing else — no JWT, no signed envelope, nothing a leaked cookie tells an attacker about you. HttpOnly, Secure on https, SameSite=Lax.

ADR-0002

Who gets in

An email allowlist, re-read on every single sign-in. Removing someone takes effect on their next request — no restart, no cache to wait out — and every change lands in an append-only audit log on the box.

Secure hosting

No inbound admin door

Editing the allowlist is the most dangerous operation there is, so it has no public endpoint at all. The box reaches out over a locked channel instead. Stated as one rule: a control-plane change never has an inbound door open to the internet.

ADR-0004

The perimeter

A Cloudflare tunnel proxies exactly one hostname to loopback and returns 404 for everything else. The app binds 127.0.0.1. Nothing listens for inbound connections — no ports forwarded, no static IP, nothing to scan.

Access model

Tenant isolation

Structural, not a permission check. Every store is rooted at the tenant’s own directory, so a store object cannot address another tenant’s data even if the code asked it to. Wrong owner and does-not-exist both return 404, so the API never confirms a stranger’s campaign is real.

Access model

Delegation attenuates

You can hand a colleague part of your own access — never more than you hold, with a spend ceiling per grant, enforced at a single gate. Rights shrink as they are passed on; they cannot grow.

ADR-0005

Headers and budgets

nosniff, frame-ancestors none, HSTS, a strict CSP on the API and no-store on every API response. Per-user quotas and spend caps return 429 rather than a surprise invoice.

Access model

What is not true yet

  • No third-party audit. No SOC 2, no ISO 27001, no penetration-test report.
  • A run launched from the web UI currently shares the API process and its environment file. The CLI runs as its own process; the split for web-launched runs is designed and not yet built.
  • Per-user API keys are designed but not shipped. Today an install carries one set of provider keys.
  • These invariants are held by typed seams and review, not by a standing regression suite.

All four are already recorded in the repo’s own architecture decisions. A security page that only listed the wins would be the one worth distrusting.