Roadmap
This is the platform’s build plan: the phases, what each one delivers, and the five decisions that have to be answered before most of them can start. One of those five is now answered; the other four are not. Nothing below is Shipped unless this page says so explicitly — the plan is a plan.
The finding that reorders everything
Section titled “The finding that reorders everything”There are two API contracts and one of them is not implemented. The desktop client
targets demostatics-pc_application/docs/API_CONTRACT.md v0.2 — /v1 paths, signed
JWTs carrying tier and features[], WebSocket streaming, usage meters, a
server-defined taxonomy, numeric MetricTick.value. demostatics-web serves /api/v1
— opaque Sanctum tokens, REST only, no entitlements, no billing, seven fixed dimension
columns, and a value column holding display strings like "48,210 MW" with no history.
The full axis-by-axis comparison is on the two contracts and is not repeated here. What matters for this page: every phase below assumes that disagreement is settled first, which is why settling it is Phase 1 and costs a week of nobody writing code.
Two consequences the roadmap states plainly:
- Billing is not flat tiers. The desktop contract specifies usage metering on top
of
free | individual | pro | enterprise. Asubscriptionstable alone cannot express “20,040 MB of 100,000 used, refills on the 1st”. - The five-dimension model from the founding spec is superseded.
API_CONTRACT.mdstates the taxonomy replaces it. Pick the taxonomy and model geography as a facet of it. Building both is how the phone and the desktop end up unable to render the same catalogue.
Decisions only you can make
Section titled “Decisions only you can make”Work is blocked or wasted until these five are answered. Each is a business or legal question, not an engineering one. The first has now been answered by founder decision; the other four have not.
1. Is Demostatics moving money, or instructing someone who does? — answered
Section titled “1. Is Demostatics moving money, or instructing someone who does? — answered”Answered by founder decision. Demostatics will advise clients, manage client capital on their behalf, and trade its own book. Holding or directing client funds makes you a regulated financial institution in most jurisdictions, and the engineering remains the small part. See Business Model for the four lines.
The old recommendation on this page — never take custody, integrate a licensed broker or PSP, store only references, and keep money schema out of the repository until a partner is under contract — is superseded.
What replaces it is not an engineering answer, it is a licensing path. Each of those activities is separately regulated in most jurisdictions; the jurisdiction itself is undecided; and the licence, not the integration, is the gate. That path has the longest lead time of anything on this page, which is why it starts in Phase 0 rather than in Phase 9. Regulatory Posture is the list of questions counsel has to answer before any of it is a plan. None of it is legal advice.
One part of the old reasoning survives and is not superseded: a half-designed money schema is a liability the day it merges rather than the day it is used. Design the money layer against a real licence and a real custodian, not against a guess about what they will require.
2. Who owns the worker backend, and will it emit numerics?
Section titled “2. Who owns the worker backend, and will it emit numerics?”The worker tier is in none of the repositories and is the hard dependency for charts, thresholds and conditional timers. See the worker tier for what is documented about it and how much of that is Not built (all of it). One conversation with whoever owns it replaces months of workaround.
3. One identity authority — Laravel or the stream backend?
Section titled “3. One identity authority — Laravel or the stream backend?”Recommended: Laravel owns users, roles, bans and verification, and mints a short-lived RS256 ticket that the stream backend verifies offline via JWKS. Anything else means a revoked phone keeps its data stream.
4. What can a free or lapsed user actually see?
Section titled “4. What can a free or lapsed user actually see?”An app that is a pure paywall over server data risks App Store 4.2 rejection, and looks broken to anyone who opens it. Forum, polls and editorial are the natural free surface.
5. Turkish now or later?
Section titled “5. Turkish now or later?”Roughly 155 user-facing strings today, by the roadmap’s count. Retrofitting localization after the next phases costs three to four times as much.
Fix now — small, real, already broken
Section titled “Fix now — small, real, already broken”These are findings in the current tree, not hypotheses. Some have been fixed; some have not.
Already fixed
Section titled “Already fixed”| Item | Where | Status |
|---|---|---|
| Tokens expire | demostatics-web/config/sanctum.php | Shipped |
| Token abilities are scoped | demostatics-web/app/Support/TokenAbility.php | Shipped |
| Scheduler registers something | demostatics-web/routes/console.php | Shipped |
/health reports silent dependencies | demostatics-web/app/Http/Controllers/Api/V1/HealthController.php | Shipped |
Desktop Feature enum is forward-compatible | demostatics-pc_application/crates/ds-core/src/identity.rs | Shipped |
Tokens now expire. demostatics-web/config/sanctum.php was 'expiration' => null, so a leaked
token was valid forever. It now reads env('SANCTUM_EXPIRATION', 60 * 24 * 30) — 30
days by default — and sanctum:prune-expired --hours=168 runs daily so dead credential
rows do not accumulate.
Tokens are scoped. createToken($deviceName) granted *. Device tokens now carry a
bounded set from App\Support\TokenAbility — data:read, forum:write,
account:write, moderate — with ingest:write deliberately withheld from anything a
person signs in with. Sanctum’s abilities and ability middleware are aliased in
demostatics-web/bootstrap/app.php so a route can enforce it.
The scheduler exists. demostatics-web/routes/console.php registered nothing. It now carries the
token prune and documents the schedule:run cron entry it needs, without which
everything scheduled fails silently.
Silent deployment failures are visible. GET /api/v1/health reports can_register
and queue alongside world_store — the dependencies whose absence leaves the API
looking healthy while a whole tier is dead.
The desktop’s Feature enum is forward-compatible. It derived Deserialize over a
closed variant list, so one unrecognised feature name failed the whole Welcome payload
and would have bricked every shipped desktop the first time the server added a
capability. Unknown names now deserialize to Feature::Unknown, which
Entitlements::can never grants. #[serde(other)] cannot do this — serde only permits
it on internally or adjacently tagged enums, and this is a plain string enum — so it
needed hand-written Serialize/Deserialize. That also fixed a second latent bug: the
derive emitted "RiskViews" while API_CONTRACT.md specifies "risk-views". Those
agreed only because both ends of the wire were the same Rust enum.
Still outstanding
Section titled “Still outstanding”Nobody can verify their email in production. MAIL_MAILER=log in both demostatics-web/.env and
demostatics-web/.env.example, while api.verified gates Reports and Database in demostatics-web/routes/api.php. Every
new signup would be locked out of the paid surface. This is credentials, not code — and
/health now answers "can_register": false until it is fixed.
Nothing drains the queue. QUEUE_CONNECTION=database with no supervised worker:
jobs are accepted and never run. On the first real purchase the customer pays and the
entitlement never lands. /health reports "queue": "requires_worker".
EnsureNotBanned deletes all of a user’s tokens. demostatics-web/app/Http/Middleware/EnsureNotBanned.php
calls $user->tokens()->delete(), which will orphan any future device-link or push-token
rows that reference personal_access_tokens. Nothing references them yet, so this is a
forward constraint on Phase 5 and Phase 8, not a bug today: design those foreign keys to
be revoked, not nulled.
The phases
Section titled “The phases”Sizes assume one full-stack engineer. Every phase is Planned except Phase 0, which is Partial — some of its engineering items are already in the tree, listed above.
Licensing has no engineering size at all and appears in none of these figures. It runs in parallel from Phase 0, its lead time is measured in months to years, and Phase 9 waits on it rather than on any row above.
| Phase | What it delivers | Size | What it unlocks | Status |
|---|---|---|---|---|
| 0 | Make it deployable, and start the licensing clock | 2–3 weeks, plus a licensing lead time that is not engineering | Everything below | Partial |
| 1 | Settle the contracts on paper | 1 week, no code | Parallel work in three repos | Planned |
| 2 | Numeric time series with provenance, from source | 4–6 weeks | Charts, thresholds, the map’s data layer, and the record-keeping everything else rests on | Planned |
| 3 | Entitlement and web billing | 3–4 weeks | Enforceable subscriptions, first revenue | Planned |
| 4 | The data product on mobile | 4–5 weeks | The app becomes worth paying for | Planned |
| 5 | Platform: settings, sessions, i18n, push | 4–6 weeks | Notifications exist at all | Planned |
| 6 | IAP and store launch | 3–4 weeks + review | The phone as a sales channel | Planned |
| 7 | Worker-evaluated alerts | 3–4 weeks | ”Tell me when the risk index crosses 60” | Planned |
| 8 | Linked devices | 2–3 weeks | Mobile connectable to the desktop | Planned |
| 8.5 | Conflict prevention in code | Not sized | Lines 2–4 running together without a structural conflict | Planned |
| 9 | Money movement | Not sized | Managed capital, the firm’s own book, timers whose action is execute | Planned, gated on licensing |
Phase 0 — Make it deployable
Section titled “Phase 0 — Make it deployable”TLS on a real domain, Postgres, a supervised queue worker, a cron entry running
schedule:run, real SMTP, Redis cache, token expiry and abilities, eas.json and a dev
client. See deployment for the current state of each.
In parallel from day 1, and not engineering: legal entity, Stripe account, public terms and privacy URLs, Apple and Google developer accounts, and the Paid Applications Agreement — which takes multiple weeks, so start it before you need it.
Engaging counsel and starting the licensing path belongs here, on day one. It is the longest-lead-time item on this page by a wide margin — months to years — and it is the one thing no amount of engineering shortens. Lines 2, 3 and 4 of the business all wait on it, and so does Phase 9. Starting it later means arriving at a finished platform that the firm still may not use to advise, manage or trade. It costs no engineering time, which is the whole argument for starting it while there is nothing else it can block.
Take Regulatory Posture to counsel as the question list. The jurisdiction is undecided and is itself the first question; the entity structure is the second. Nothing on that page is legal advice.
Unlocks: everything. Nothing below is safe or testable without it, and nothing in lines 2 through 4 can start until the licensing clock does.
Phase 1 — Settle the contracts on paper
Section titled “Phase 1 — Settle the contracts on paper”No code. Amend both contract documents to agree on: the identity authority and its
single token-exchange endpoint; one feature vocabulary; one 403 body with a stable
code; one device registry; one dataset identifier namespace shared with the worker;
taxonomy over fixed dimensions; and who owns usage.meters.
Unlocks: parallel work in three repositories without divergence. This is the cheapest phase on the list and the one that prevents the most rework.
Phase 2 — Numeric time series, from source
Section titled “Phase 2 — Numeric time series, from source”metrics, metric_observations, metric_rollups, a units table. Rollups are
computed by the worker and written in, not derived by Laravel on a schedule. An ingest
endpoint behind a scoped ingest:write ability with Idempotency-Key. The worker
writes numbers; database_items becomes a projection so the existing
GET /api/v1/database-items stays byte-identical.
Convert the seven seeded rows in demostatics-web/database/seeders/DatabaseItemSeeder.php by hand. Do
not ship a regex parser to recover values that the worker already holds as f64.
Provenance is part of this phase. It was previously treated as a Phase 4 display detail — something a chart shows underneath itself. It is now a record-keeping requirement, because a figure that an advisory call, a managed portfolio or the firm’s own book was acted on has to be reconstructible years later. Every observation carries:
- the source it came from,
- the method and the method version that produced it, so a revised model is distinguishable from a revised input,
- measured-at and ingested-at, because the lag between reality and knowledge of it is itself a fact about the number,
- an append-only revision history, so a correction never erases the value someone acted on,
- and enough of both to support point-in-time reconstruction — “what did this series say at 14:02 on the day of the trade” answered from the tables, not inferred.
None of that exists today. database_items has measured_at plus Laravel’s
created_at/updated_at and no source, method or version column at all; reports has
published_at and the same gap. Neither has any revision history.
Say this plainly: it is a column set while these tables are being written, and a backfill of every value ever published or acted on once they exist. Cheap now, expensive after. See Business Model for why it stopped being cosmetic and Regulatory Posture for the record-keeping obligation behind it.
Unlocks: every chart, every statistic, every threshold, conditional timers, “premium analytics” as a sellable thing, and the map’s data layer. This is the technical unlock for the whole product, and it is the phase where the platform stops being a display tier and becomes a system of record.
Phase 3 — Entitlement and web billing
Section titled “Phase 3 — Entitlement and web billing”plans, features, plan_feature, subscriptions, entitlement_grants,
subscription_events. One resolver and one api.entitled:{feature} middleware shaped
exactly like the existing api.verified, with a machine-readable flag in the 403 so a
client can tell “upgrade” from “verify” from “forbidden”. Gate in SQL, never
post-filter. RS256 exchange plus JWKS so the desktop shares the same entitlement.
Sell via Stripe on the website, not in-app purchase.
Unlocks: “only if a subscription exists” becomes true and enforceable; first revenue with zero store commission and no App Review on the critical path.
Phase 4 — The data product on mobile
Section titled “Phase 4 — The data product on mobile”Catalogue to detail (sparkline, chart, provenance — the display of what Phase 2 already
records) to table. Cursor pagination.
Server-side downsampling matching the desktop’s LTTB so both clients draw the same
curve. Saved views and watchlists as one shared server-side table, designed with the
desktop in the room — this is the object that pairing needs later. Locked rows are
soft-gated; ErrorState grows one entitlement branch and the paywall then appears
everywhere for free.
Calculator stage 1: position sizing, cost to open, break-even, R-multiple — server-computed, integer minor units, no money rail.
Unlocks: the app becomes worth paying for.
Phase 5 — Platform
Section titled “Phase 5 — Platform”Settings split two ways: device-local (theme, biometric lock, cellular use, cache)
versus account-level and synced (default filters, units, base currency, notification
preferences, thresholds). A sessions list with per-device revoke, which is nearly free
because personal_access_tokens already stores device names and last_used_at. i18n
extraction now, not later. Then push: device_tokens, per-category preferences, fanout
with receipt handling.
Unlocks: notifications exist at all — the precondition for alerts and for any confirm-style flow anywhere in the product.
Phase 6 — IAP and store launch
Section titled “Phase 6 — IAP and store launch”RevenueCat as the store-rails adapter with Laravel still the authority. Localized prices, restore purchases, sandbox and production isolation, server-side receipt validation.
Deliberately last of the revenue work: Stripe already earns by then, and this is the path gated on someone else’s review queue.
Unlocks: the phone as a sales channel.
Phase 7 — Worker-evaluated alerts
Section titled “Phase 7 — Worker-evaluated alerts”The honest version of “timers”. Rules are stored and served by Laravel; evaluation runs
in the worker tier. Conditional threshold rules over the Phase 2 series: edge-triggered,
with hysteresis, debounce, staleness gates, and no retroactive firing when history is
backfilled. The action is notify. The same rule object as the desktop’s
ds-core::alerts::AlertRule (demostatics-pc_application/crates/ds-core/src/alerts.rs), so a rule set on the phone
shows up on the desktop.
Unlocks: “tell me when the risk index crosses 60” — the actual product promise. It also exercises every mechanism a money-moving timer needs (scheduler, occurrence keys, fail-closed behaviour, append-only audit) at zero regulatory exposure.
Phase 8 — Linked devices
Section titled “Phase 8 — Linked devices”linked_devices keyed on a set of devices per account, each with a capability set — not
a 1:1 desktop-to-phone pair. Short-lived typed code, roster, heartbeat, revoke. Handoffs
(“send this view to my phone”) as an explicit pull, never screen mirroring. Revocation
must never be entitlement-gated.
Unlocks: the brief’s “connectable to the PC application”, built on top of objects that already exist by then rather than invented for it.
Phase 8.5 — Conflict prevention in code
Section titled “Phase 8.5 — Conflict prevention in code”Planned. Not sized, because it depends on answers that do not exist yet. It sits here because it is a precondition for Phase 9, not a follow-up to it.
Running lines 1 through 4 at once creates conflicts of interest that regulators generally require to be structurally prevented rather than disclosed: the firm trading on data it also sells, trading alongside accounts it manages, and advising clients whose positions it may be on the other side of. The full list is on Regulatory Posture. Three of them are engineering work:
- Information barriers as access control. “The trading function cannot see what the advisory function is working on” is a permission model inside the platform, not a policy paragraph. It reuses the Phase 3 entitlement resolver, but the subjects are staff and internal functions rather than subscribers.
- Deterministic, logged order allocation. When one idea produces orders for both the firm’s book and managed accounts, which was filled first and at what price has to be decided by a rule the system applies and records — not by whoever submitted first, and not reconstructed afterwards from memory.
- Publication-versus-trade timestamp ordering. “Did you trade ahead of your subscribers” is answerable only if publication times and the firm’s own order times are recorded on one clock and an ordering rule is enforced between them. Which rule that is remains a founder decision; the options are set out on Business Model.
All three rest on Phase 2 provenance and Phase 3 entitlements, and none of them can be retro-fitted once there are orders to reconstruct.
Unlocks: lines 2, 3 and 4 running together without a conflict that only a disclosure paragraph stands between.
Phase 9 — Money movement
Section titled “Phase 9 — Money movement”Mandatory, not conditional. Managing client capital and trading the firm’s own book cannot exist without it. What changed is not whether this phase happens but what it waits on: a licence, not a contracted partner found by engineering.
Append-only ledger, integer minor units, idempotent submit, quote-bound pricing with
expiry, step-up re-authentication verified server-side against a device-enrolled key,
limits and a cooling-off window, and reconciliation for the request-sent-response-lost
case. Every one of those still applies, and each matters more now that this is a certainty
rather than a maybe. Then, and only then, timers whose action is execute rather than
notify.
Mapping
Section titled “Mapping”Mapping slots in after Phase 2 and needs its own decision, because someone must own
sourcing real geometry and costing tiles. map-overlays is already a feature name in
the desktop’s entitlement set (demostatics-pc_application/crates/ds-core/src/identity.rs), so the gate exists.
Which tier computes what
Section titled “Which tier computes what”demostatics-web/README.md states that the display tier “does not gather or process
anything”. Two phases above brush against that line, so it is worth being exact about
where it falls. This complements the data pipeline.
- Request-scoped arithmetic belongs in Laravel. One user asks a question and the server answers it from current state: a quote from current rates, pagination, a vote tally, an export. The transaction calculator is this. It must be server-computed and server-authoritative — a phone that works out fees from a cached rate shows a number you did not charge and leaves no audit trail — with versioned rates, and a quote id plus expiry binding the price shown to the price executed.
- Continuous processing over the dataset belongs to the worker tier. Evaluating threshold rules across every series on a schedule, and computing rollups, are pipeline work. Putting them in Laravel would quietly turn the display tier into a compute tier, and split “when did this cross a line” from the forecasting that answers “what happens next”.
So Laravel stores alert rules, serves them, and notifies; the worker decides when one fires. The desktop client keeps doing the heavy statistics, forecasts and simulation locally, and the phone never attempts any of it.
Honest scoping
Section titled “Honest scoping”Summed end to end, this is roughly 9 to 18 months for one full-stack engineer across three languages and three repositories, plus legal, two store accounts, a custodian or broker relationship, and a worker backend that does not exist in any of these repositories.
That figure covers the data product — line 1 — and nothing else. It was written when the firm was not going to touch capital. Lines 2, 3 and 4 add licensing, a compliance function, the conflict-prevention work of Phase 8.5 and the money layer of Phase 9 on top of it.
No revised total is offered here, deliberately. The binding constraint is no longer an engineering estimate at all: the licensing timeline is set by a regulator and by counsel, is measured in months to years, and is not shortened by writing code faster. Anyone who adds a number to the 9-to-18 months and calls the result a schedule has estimated the part that was never the problem. See Regulatory Posture.
The failure mode of attempting it as one push is not lateness. It is six half-built subsystems, each blocked on a different missing prerequisite.
The smallest coherent thing that is genuinely useful is Phases 0 through 4. Numeric data with history and provenance, browsable and charted on the phone, behind a working paywall, on a real deployment, sold from the web. That is a product. Everything after it is additive, and each later phase gets easier because the phase before it built the object it needs. It is also the only stretch of this plan that is required under every possible answer counsel comes back with, which makes it the safest thing to build while the licensing clock runs.