Skip to content

Router.Africa — Planning & Documentation Backlog

A working to-do list for the 9 documentation/scoping items, with what each one actually requires, how they depend on each other, and a suggested execution order. This is the backlog itself — each item below becomes its own deliverable.


Historical index — partially superseded (annotated 2026-09-19, status refreshed 2026-09-20). The original text below is preserved unchanged for sequencing and dependency history. For current state, use the status table here; for decisions, ../architecture/decisions-log.md is the source of truth. Note the body still refers to apps/queue-consumer (item 4) — that Worker was folded into apps/dashboard-api as a queue() handler (decisions log → Queue consumer placement).

# Item Status Where it lives now
1 OpenRouter reverse-engineering Done (rev 2) ../product/openrouter-api-comparison.md
2 Tech stack doc Done ../architecture/tech-stack.md
3 Frontend scope Done — screens in dashboard-scope, stack in tech-stack ../product/dashboard-scope.md
4 Backend scope Done (short form, 2026-09-20). Service map, data map, request flows, scheduled jobs and failure behaviour. Small items to confirm before the schema (org vs workspace level of the content opt-out and hold toggle, spend cap counters, queue retry) are listed at its end ../architecture/backend-scope.md
5 Decisions log Done, living. No open decisions as of 2026-09-20; deferred work is in the future-optimizations register ../architecture/decisions-log.md
6 DB schema Drafted (2026-09-20), review pending. Column-level spec, ER overview and hot-path reasoning in db-schema.md. Three small items noted for the build (input_allowance unit, top-up fees per vendor and currency, minimum hold and rounding step per currency). Follow-ups it created are folded into the decisions log and backend scope: workspace discounts, hold close-out, Niobi top-up status check, invariant check ../architecture/db-schema.md
7 Diagrams Partial — ../product/architecture.svg regenerated 2026-09-19 to reflect current decisions; the full diagram set (request flow, hold/settle state machine) is still outstanding ../product/architecture.svg
8 Wiki Not started
9 Execution plan Not started
10 Developer API docs Scoped (2026-09-20). Scope, dependencies and home decided (hand-written, router.africa/docs, website repo); docs themselves not started. Endpoint list proposed in ../product/api-surface.md; error catalogue to be defined before the apps/api build; status page at launch; rates docs deferred ../product/api-docs-scope.md
11 Billing / pricing model Done; launch markup values are set later in the admin app (not a blocker) ../product/pricing-model.md
12 Testing strategy Captured as a table in the tech stack doc; not yet split out. Add: Bifrost sync failure/rollback paths and the discrepancy view ../architecture/tech-stack.md
13 CI/CD pipeline Decided (one decisions-log row); standalone doc not written ../architecture/decisions-log.md
14 Observability plan Tooling decided (Datadog); signals/alerts plan not written ../architecture/decisions-log.md
15 API key security Decided (decisions-log rows, including the pending-then-active key creation order and Bifrost sync); standalone doc not written. Scope note: item 16 depends on it as “compliance”, but data residency/KYC/AML were carved out ../architecture/decisions-log.md
16 Launch scope Done. Markets decided (all Niobi markets live at launch); the v1 model list is admin-configured, with a model “on” only when it has a published rate and staff enable it ../architecture/decisions-log.md
17 Content-logging launch requirements (must not be forgotten) Decisions closed; build to-dos open across frontend, backend and website See the section below; decision in ../architecture/decisions-log.mdAudit trail & prompt/response retention
18 Cleanups and verifications Open, small See the section below

Goal: Extract everything publicly knowable about OpenRouter’s product surface (routing behavior, API contract, pricing model, failure handling) to inform what Router.Africa’s API needs to replicate or intentionally diverge from.

Note on scope, from earlier discussion: OpenRouter’s internal architecture (infra, DB, deployment) isn’t public and isn’t the useful target — their routing policy and API contract is, and that’s fully documented. This item should produce a comparison table: OpenRouter behavior → Router.Africa equivalent (via Bifrost) → gap, if any.

Depends on: nothing — can start immediately.


Goal: Single source of truth for every stack decision made so far, with the rationale, so it’s not scattered across chat history.

Should capture, per component: what was chosen, what alternatives were considered, why they were rejected, and any open caveats (e.g., cluster sizing still needs load-test data).

Depends on: nothing — this conversation already has the content; it’s a writing/organizing task.


Goal: Define what the dashboard actually needs to do — screens, data views, user flows — building on the existing Vite service once its stack details are shared.

Known requirements so far (from earlier scoping): usage analytics, request logs, cost breakdown by model/provider, billing/credit balance view, API key management, account/session auth (distinct from API-key auth).

Depends on: the Vite stack details (you said you’d share these) — worth starting the requirements/flows now and slotting in the technical specifics once that’s available.


Goal: Concrete service boundaries and responsibilities for the Node/TS Worker(s): what apps/api handles, what apps/queue-consumer handles, what data each touches.

Should resolve the open items flagged earlier: idempotency handling, mid-stream disconnect/settle policy, Bifrost timeout/retry policy, rate limiting.

Depends on: #5 (architecture decisions) should be finalized in parallel, since backend scope and open decisions are tightly coupled.


5. Open + finalized architecture decisions log

Section titled “5. Open + finalized architecture decisions log”

Goal: A living decisions record — separate from the tech stack doc (#2) — specifically tracking things that are decided (e.g., no queuing on the Bifrost call path, atomic UPDATE for hold/settle) versus still open (idempotency key design, disconnect/settle policy, rate limiting thresholds, CI/CD approach, data residency).

Format suggestion: simple two-column running log — Decided / Open — so nothing raised in conversation quietly gets lost (idempotency is the example of exactly this happening once already).

Depends on: nothing — start immediately, feed into #4.


6. Database schema / information architecture

Section titled “6. Database schema / information architecture”

Goal: What you’re describing is data modeling — the entities (accounts, API keys, holds, settlements, usage events, orgs/teams) and their relationships, before writing actual SQL. “Information architecture” more precisely refers to how information is organized/labeled/navigated (more of a UX term) — for this item you likely want schema design, plus a lighter IA pass for the dashboard’s navigation/data organization as part of #3.

Should cover: core tables (accounts, balances, holds, settlements, api_keys, usage_events), relationships, indexing strategy for the hold/settle hot path, and what goes in PlanetScale Postgres vs. what goes to the analytics store.

Depends on: #4 (backend scope) — schema follows from what the service actually needs to read/write.

Update (2026-09-20): the decisions #4 was meant to resolve are closed, so the schema is unblocked. Additional coverage it now needs: key rows with Bifrost sync status, allowed_models, admin and user spend caps, rate limits; the model “on” gate; markup rules and top-up fees; the content-logging flag with consent timestamp and terms version; request log and audit events (including reads of stored content) in one store; and the hooks checklist in ../architecture/future-optimizations.md.


Goal: Visualize the architecture — request flow (Worker → hold → Bifrost → settle → queue), system topology (Cloudflare + PlanetScale + K3s/Bifrost), and the hold/settle state machine.

Depends on: #2 and #5 being reasonably settled, so diagrams reflect final decisions rather than needing rework. Also feeds into #8 (the wiki should embed these).


Goal: A reference doc explaining the tools and concepts used across this stack (K3s, k6, Durable Objects, Hyperdrive, Bifrost’s routing model, etc.) — written for the team, not just for you, so onboarding doesn’t require re-deriving this conversation.

Should embed: the diagrams from #7 directly rather than duplicating explanations in prose.

Depends on: #7 for embedded visuals; otherwise can start in parallel with everything else as a glossary that grows over time.


Goal: Sequenced build plan — what gets built first, what blocks what, rough milestones.

Depends on: everything above. This is deliberately last — an execution plan built before the schema, scope, and open decisions are settled would just need to be rewritten.


Goal: External documentation a third-party developer reads to integrate — endpoints, auth, request/response shapes, error codes, rate limits, code samples. Distinct from everything above, which is internal planning.

Depends on: #4 (backend scope) and #1 (OpenRouter comparison, since the API contract likely mirrors it closely) — needs both settled to document accurately.

Update (2026-09-20): #4 and #1 are done, so drafting can start. Scope, content list, dependencies and the hosting decision are in ../product/api-docs-scope.md. Still needed before it can be finished: the error catalogue and rate-limit response contract (define with the apps/api build), the list of extra endpoints at launch, and the schema (#6) for any endpoint that returns ledger or key data. Decided: hand-written and curated (no generated reference), hosted at router.africa/docs, source in the website repo, built with Astro Starlight (a short spike first to check the site’s i18n, sitemap and Cloudflare adapter alongside it). Drift handling deferred. It also feeds work elsewhere: dashboard and website links.


Goal: How credits convert to local currency, margin over Bifrost/provider costs, top-up flows through Niobi’s payments rails, and FX handling if multiple African currencies are in scope. Currently assumed throughout the architecture but never actually specified.

Depends on: nothing technical — this is a business/product decision that the hold/settle mechanism (#4, #6) needs as an input, not the other way around. Worth resolving early since it shapes the schema.


Goal: Pull the testing approach (Vitest, @cloudflare/vitest-pool-workers, k6, the hold/settle concurrency-correctness test) out of the tech stack doc into its own standing reference for the team.

Depends on: #2 (tech stack doc) — split out once that exists.


Goal: How code moves from PR to deployed Worker / Bifrost config change / dashboard release.

Depends on: #9 (execution plan) is a reasonable pairing — sequencing and deployment mechanics are related decisions.


Goal: What signals surface a problem (queue consumer falling behind, PlanetScale write latency climbing, Bifrost errors) before customers notice.

Depends on: #4 (backend scope) and #6 (schema) — you need to know what’s being built before deciding what to monitor.


Goal: Ensure API keys never leak or get misused — generation, storage, rotation, scoping, and revocation.

Scope note: narrowed from a broader security/compliance review. Data residency and KYC/AML are explicitly out of scope here — Router.Africa relies on Niobi’s existing API signature/auth mechanism for that layer, so it isn’t a separate item for this backlog.

Should cover:

  • Key generation (sufficiently random, prefixed for identifiability e.g. ra_live_... / ra_test_...)
  • Storage — never store raw keys; store a hash (e.g. SHA-256) and match on hash at auth time, same principle as password storage
  • Scoping — per-key permissions/limits (e.g. spend cap per key, not just per account) so a leaked key has bounded blast radius
  • Rotation & revocation — how a user invalidates a compromised key without downtime, and whether old keys get a grace period or die immediately
  • Transmission — enforced HTTPS only, keys never logged (a real risk given the logging/analytics pipeline already in the design — worth an explicit rule that request logging redacts the key)
  • Detection — anomaly signals worth catching (sudden volume spike from one key, requests from unexpected patterns) that could indicate a leaked key in active misuse, tying into the rate-limiting item already flagged as open in #5

Depends on: #4 (backend scope) and #6 (schema) — key storage/scoping is part of the account/API-key table design.


Goal: Which markets ship first, which models are in the v1 list. Distinct from everything else in this backlog, which is how to build it — this is what ships first. Currently an open decision per existing project notes.

Depends on: #11 (billing/pricing, since currency support ties to market selection) and #15 (compliance, since market selection may be regulatory-constrained).

Update (2026-09-20): resolved. All markets live with Niobi at launch; the model list is admin-configured, not a fixed v1 list (decisions log → Launch scope, v1 model list).


17. Content-logging launch requirements (do not forget)

Section titled “17. Content-logging launch requirements (do not forget)”

Why this exists: prompt/response content is stored by default for every account, with opt-out, and retained indefinitely for now (decisions log → Audit trail & prompt/response retention). That is a deliberate but risky default, and these follow-ups must be closed before launch or consciously accepted.

  • Disclosure that content is saved by default — build in all three places:
    • Frontend (apps/dashboard): clear notice in the signup flow that prompts and responses are saved by default, with how to opt out; the opt-out toggle in Account Settings (on by default).
    • Backend (apps/dashboard-api, apps/api): per-account content-logging flag defaulting to on; record the consent timestamp and terms version at signup; audit-log every toggle change; opt-out stops future capture only (purging existing content is deferred); the content write path checks the flag.
    • Website (separate Astro repo): terms of service and privacy policy state plainly that content is saved by default and how to opt out.
  • Access control (build requirement): stored content is readable only by admins, both Router.Africa staff admins and customer org admins; members and other roles get no access. Every read of stored content is audit-logged (confirmed).
  • Bifrost content logging off (backend/infra): set disable_content_logging in infra/bifrost and verify in staging that no content lands in Bifrost’s Postgres.
  • Datadog (backend/infra): confirm prompts and responses are not captured in traces.
  • Storage estimate: with indefinite retention, estimate R2 growth against expected traffic before launch.

Decided, no longer open: no PII redaction in v1 (bodies only, never headers or keys); audit trail shares the request-log store. Deferred to ../architecture/future-optimizations.md: retention window, customer deletion, purge on opt-out.


Small items surfaced while closing the decisions. None blocks the schema.

Doc cleanups

Verify against the running Bifrost instance

  • Usage reconciliation: confirm that an x-request-id header on a chat completion becomes the Bifrost log id, that GET /api/logs accepts start_time, end_time and min_tokens as expected, and that log rows carry the virtual key id (see the decisions log, Usage reconciliation).
  • The endpoint the admin app will read to list configured models (docs point to /api/models/details; not confirmed).
  • Whether BIFROST_MODEL_PARAMETERS_URL is the same setting as the pricing sheet URL (PricingURL). Only matters if egress is ever restricted.
  • Bifrost’s per-key allowed_models, rate-limit and budget fields, so the sync path pushes the right shape.

Build reminders from decisions

  • Model “on” gate: a model needs a published rate and explicit staff enable; provider keys use explicit model lists, never ["*"].
  • Key creation order: pending row in Postgres, then Bifrost virtual key, then active, then show the key. Admin discrepancy view and periodic reconciliation job.
  • Admin spend cap is a fixed USD backstop in Bifrost and won’t follow FX moves; the user cap is enforced in Postgres in credits.

Where things stand (2026-09-20): #1, #2, #3, #5, #11 and #16 are done. The next chain is: #6 schema (the short #4 service map is done), then #7 diagrams (request flow, hold/settle state machine), then #14 observability plan, #10 developer API docs, #12/#13 standalone docs, #8 wiki and #9 execution plan. #17 build to-dos and #18 cleanups run alongside. The ordered list below is the original and is kept for history.

  1. #5 (decisions log) — start now, low effort, prevents further items like idempotency from getting lost
  2. #11 (billing/pricing model) — start now; shapes schema and market decisions downstream
  3. #2 (tech stack doc) — write up what’s already been decided in this conversation
  4. #1 (OpenRouter reverse-engineering) — independent, can run in parallel with 2/5
  5. #16 (launch scope) — once #11 has enough shape to constrain it
  6. #4 (backend scope) — resolves the open decisions from #5 into concrete service design
  7. #6 (DB schema) — follows directly from #4 and #11
  8. #15 (API key security practices) — alongside #6, since key storage/scoping is part of schema design
  9. #3 (frontend scope) — can start now on flows/screens, finalize once Vite stack details land
  10. #14 (observability plan) — once #4 and #6 exist
  11. #10 (developer-facing API docs) — once #4 and #1 are settled
  12. #12 (testing strategy doc) — split out of #2 once that exists
  13. #7 (diagrams) — once 2/4/5/6 are stable enough to draw accurately
  14. #8 (wiki) — folds in #7’s diagrams
  15. #13 (CI/CD & deployment pipeline) — pairs with #9
  16. #9 (execution plan) — last, once everything above exists

Status (2026-09-20): items 1, 2, 3, 4 (short form), 5, 11, 16 done; 12–15 decided but not yet written up as standalone docs; 7 partial; 6 next; 8–10 outstanding; 17 and 18 are open build to-dos and cleanups. See the status table at the top.