Simulator reference¶
reposix-sim is the in-process axum HTTP server that backs every demo, unit test, and autonomous agent loop in the project. It speaks the same wire shape that the real backends (GitHub, Confluence, JIRA) speak — far enough that BackendConnector implementations can swap between sim and real without code changes.
The simulator is the default backend per project Operating Principle "simulator-first". Real backends are gated behind REPOSIX_ALLOWED_ORIGINS and explicit credential env vars (see Testing targets).
What it is¶
- A standalone binary at
crates/reposix-sim/(cargo run -p reposix-sim). - An axum 0.7 router exposing the issue-tracker REST shape (
/projects/<slug>/issues,/issues/<id>, etc.) plus a/healthzprobe. - A SQLite store (bundled
rusqlite, WAL mode) holding the issues table and the append-onlyaudit_eventstable. - A deterministic seed loader (
crates/reposix-sim/fixtures/seed.json) that lets every test run from a known starting state.
Latency envelope on the dev host: 9 ms to list issues, 8 ms to get one issue, 8 ms to PATCH one — see latency. The sim is the lower bound for transport overhead; treat real-backend numbers as the upper bound.
Run it¶
cargo run -p reposix-sim
# 2026-04-24T12:00:00.000Z INFO reposix_sim: reposix-sim listening addr=127.0.0.1:7878
The default bind is 127.0.0.1:7878. The reposix init sim::<slug> command wires that address into the helper URL — keep the port if you want reposix init to "just work" with no extra flags.
To seed the canonical demo project at startup:
The fixture defines a single project (demo) with five issues; that's the project the first-run tutorial targets.
Configuration¶
| Flag | Default | Purpose |
|---|---|---|
--bind |
127.0.0.1:7878 |
Listen address. Use 127.0.0.1:0 for an ephemeral port (tests). |
--db |
runtime/sim.db |
SQLite file path. Created if absent. Ignored when --ephemeral is set. |
--seed-file |
— | Path to a seed JSON. Without one, the DB starts empty. |
--no-seed |
off | Skip seeding even if --seed-file is provided. |
--ephemeral |
off | Use :memory: SQLite — every restart is a clean slate. |
--rate-limit |
100 |
Per-agent request quota (req/sec). Tune down to exercise rate-limit code paths. |
There is no auth on the sim. Anyone who can reach the bind address can read and write issues — that's deliberate (it removes a category of test-only ceremony) and it's why the sim should never bind to a non-loopback address.
Where the data lives¶
- Sim DB —
runtime/sim.dbby default,:memory:when--ephemeral. This holds the issues table and the sim's ownaudit_eventstable (one row per HTTP request through the audit middleware). - Cache DB (helper-side) —
<XDG_CACHE_HOME>/reposix/sim-<project>.git/cache.db(or<root>fromREPOSIX_CACHE_DIR). This is the helper's audit log — what reposix did against the sim — separate from the sim's own audit of incoming HTTP. Both tables are append-only viaBEFORE UPDATE/DELETE RAISEtriggers.
The two audit logs answer two different questions: the sim's audit_events shows what hit the sim; the cache's audit_events_cache shows what the helper attempted. They're disjoint by design — see the trust model for the full ops vocabulary.
REST surface¶
Routes the sim implements (full reference in crates/reposix-sim/src/routes/):
GET /healthz— liveness probe.GET /projects/GET /projects/<slug>— project listing + lookup.GET /projects/<slug>/issues[?since=<rfc3339>]— list issues, with optionalsincefor delta sync (matches theBackendConnector::list_changed_sinceshape).GET /projects/<slug>/issues/<id>— single issue.POST /projects/<slug>/issues— create.PATCH /projects/<slug>/issues/<id>— update; supportsIf-Match: "<version>"for optimistic concurrency.DELETE /projects/<slug>/issues/<id>— real delete.
The shape is documented in the HTTP API reference; the sim is the canonical implementation of that document.
Limitations (deliberate)¶
- No real auth. No tokens, no OAuth dance. Use
REPOSIX_ALLOWED_ORIGINSto keep traffic on loopback. - No rate limiting beyond a per-agent token bucket. No back-pressure that mirrors GitHub's
x-ratelimit-remainingor Atlassian'sRetry-After. - No webhooks, no streaming. The sim is a request/response server. Subscribe-style sync against real backends is not modelled.
- No multi-tenant isolation. One sim process serves one DB. Spawn separate processes if you need separate state for separate test runs.
- Tainted by default. Even though the sim is local, every byte returned counts as untrusted input — seeds are authored by agents and the simulator is part of the trifecta surface. The trust model covers why.
Where it's used¶
- The
cargo test --workspacesuite spins ephemeral sims viarun_with_listeneron127.0.0.1:0. scripts/dark-factory-test.sh simruns thereposix-agent-flowregression against a fresh sim.- The first-run tutorial is the user-facing entrypoint.
- Latency capture (
scripts/latency-bench.sh) measures sim numbers as the lower bound published in latency.
See also¶
- HTTP API reference — full route + payload spec.
- First-run tutorial — five-minute walkthrough against the sim.
- Testing targets — how to graduate from sim to a real backend.
- Trust model — the simulator is treated as tainted input despite being local.