Files
keysat-root/ROADMAP.md
T
Keysat 17b6749254 Drop Zaprite contact dedup-cache item (adjudicated DROP)
Owner confirmed the lean-DROP verdict without the sandbox check: the harm is
cosmetic (duplicate rows in the operator's Zaprite contact list) and the fix is
HIGH blast radius on the money path. Recoverable from git history if real
recurring revenue ever makes it worth it.
2026-06-18 07:34:38 -05:00

5.7 KiB
Raw Blame History

ROADMAP — Keysat

Longer-term backlog. Near-term state lives in AGENTS.md → Current state.

Payments & subscriptions

  • Per-profile SMTP override (schema fields exist from the keysat-smtp-emails plan; needs the form + send path).
  • Rail-preference editing UI — only matters when two providers on one profile both serve the same rail; settable today via PUT /v1/admin/merchant-profiles/:id/rail-preferences/:rail.
  • Auto-charge silently lapses a subscription on a 200-with-failure response (money-path bug; elevated above the other parked payments items). try_auto_charge_zaprite returns Ok(true) on any HTTP 2xx (subscriptions.rs:1403-1410), reading the order status only for a log line. If Zaprite returns 200 carrying a FAILED/DECLINED/EXPIRED order status, the daemon fires auto_charge_initiated and then waits for an order.paid webhook that never arrives — the subscription silently lapses, no error surfaced, the customer churns. Safe fix (no production data needed): treat any non-PAID terminal order status as not-success and fall through to the manual-pay path — a conservative fail-safe, ~10 lines + a mock-provider test. (Found during the 2026-06-17 adjudication; it replaces the old "harden Zaprite failure-body shapes" item, which was already satisfied for non-2xx responses — those route correctly to WARN + auto_charge_failed audit + webhook + manual-pay fallback.)

Agent compatibility & scoped API keys

  • Agent-delegable payment-provider connect (approved, not urgent — see plans/agent-payment-connect-scope.md). Add an à-la-carte payment_providers:write scope (never bundled into merchant-onboard), gated by a daemon-level sandbox-mode flag as the outer gate (production daemons reject scoped connect entirely) with a network gate inner defense (regtest/testnet/signet only, fail-closed to mainnet). BTCPay network is derived from an on-chain address prefix (no server/info field exists).
  • Onboarding doc-harness — Stage 2 (Path 2, regtest buyer-pays). Gated on slices 35 above. Stage 1 (Path 1, no payments) shipped completed-clean this session — harness at licensing-service-startos/onboarding-harness/, record in its STAGE1-RESULT.md. Stage 2 reuses the harness but boots the fixture with KEYSAT_SANDBOX_MODE on, stands up a Dockerized BTCPay regtest stack (bitcoind regtest + NBXplorer + Postgres + BTCPay) as additional disposable infra, and grants the agent merchant-onboard + payment_providers:write. Goal: the agent connects BTCPay (regtest) over the API and drives a test buyer payment that activates a license, with zero master-key steps. The walkthrough must be explicitly labeled regtest/test-network and must state that connecting a real mainnet wallet is the one operator-reserved step by design (a key that can redirect funds stays with the human) — a security feature, not a gap.

Packaging & distribution

  • Start9 Community Registry submission — a 2026-06-17 spec check found the wrapper passes the functional criteria (manifest, interfaces, health check, backup/restore, BTCPay dep, actions). Remaining gap before submission: add a prepare.sh to set up a clean Debian box for the first build (copy the one from hello-world-startos), then run the on-box manual verification (install / backup / restore / logs). Submission criteria themselves remain unpublished; reach out to Start9 when ready. (Icon-render and the source-available license are intentionally not treated as blockers.)

Licensing model

  • Evaluate Elastic License v2 vs the current custom LicenseRef-Keysat-1.0 (parked decision).

Validation

  • Re-test KEYSAT_INTEGRATION.md against a fresh downstream app to confirm a clean one-shot SDK integration.
  • Add an automated regression test for multi-profile webhook routing (adjudicated 2026-06-17 → DO, low blast radius — replaces the parked "manual Zaprite sandbox pass"). The routing is a deterministic provider-id→profile primary-key lookup with an anti-forgery re-fetch backstop, so the manual sandbox ceremony isn't worth it — but the path-keyed route (/v1/{provider}/webhook/:provider_idhandle_for_provider) currently has zero automated coverage on the money path. Plan: in tests/api.rs, reuse the two-provider fixture (~:3958), POST a Settled webhook to /v1/zaprite/webhook/{provider-A-id}, assert only profile A settles (B untouched; an unknown path-id 404s). Existing mock seam, no external account, runs in cargo test. Effort S.

Design (contract conformance)

The brand contract lives in design/DESIGN.md + design/tokens.tokens.json (distilled 2026-06-16 from the prior Claude Design system, archived in design/_imports/). A design-checker audit (2026-06-16) found high fidelity overall. Adjudicated 2026-06-17: the structural palette-consolidation and the token-gap nitpicks were dropped — the consolidation can't actually remove the duplication it targets (the rust-embedded admin SPA can't @import a shared file, so "consolidation" is a verbatim re-copy), and the token-gap list was partly mis-specified by the audit. Only the three contract-"never" blockers survive.

Blockers — approved to fix (adjudicated → lean DO; wants an owner glance since they change public landing + admin visuals). Three reversible CSS one-liners:

  • Gold used as an actionable fill (contract: gold is accent/border only, never a fill). (a) admin SPA .featured-pill-toggle.onweb/index.html:417-419; (b) admin sidebar upgrade CTA #tier-banner-ctaweb/index.html:537. Fix to navy-fill or gold-border/text.
  • Primary buy CTA uses pill radius 999px (contract: buttons are r-md 8px; pill is badges-only) — keysat-xyz-landing/index.html:390. Set to 8px.