# 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`. - Keysat-side dedup cache for Zaprite contacts (same buyer purchasing recurring twice can create duplicate Zaprite contacts). - Zaprite declined-card / expired-profile failure-body shapes are undocumented — harden `try_auto_charge_zaprite` once observed in production. ## 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). Feeds the doc-harness Path 2 (regtest buyer-pays). Ships after doc-harness Path 1. ## Packaging & distribution - Start9 Community Registry submission — criteria are unpublished; contact Start9 directly when ready. - `registry-landing` repurpose — currently a stub; decide whether it becomes a real public page. ## 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. - End-to-end Zaprite sandbox pass on the multi-merchant-profile webhook routing before relying on it in production. ## Design (contract conformance) The brand contract now lives in `design/DESIGN.md` + `design/tokens.tokens.json` (distilled 2026-06-16 from the prior Claude Design system, now archived in `design/_imports/`). A `design-checker` audit (2026-06-16) found high fidelity overall, with these items where the **code contradicts the contract's stated rules** or bypasses the token scale: **Blockers (code violates a named "never" rule):** - Gold used as an actionable *fill* (contract: gold is accent/border only, never a fill). (a) admin SPA `.featured-pill-toggle.on` → `web/index.html:418`; (b) admin sidebar upgrade CTA `#tier-banner-cta` → `web/index.html:537-542`. 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:384-385`. Set to 8px. **Structural (headline):** - All four surfaces inline their own copy of the CSS variables instead of importing the canonical `design/brand/palette.css` (landing :33-56, registry :11-22, docs.css :7-21, admin :9-25). Copies are currently exact but one edit from drift. Consolidate onto `palette.css`. **Token gaps / drift (decide: tokenize the as-built value, or snap to an existing token):** - `14px` card radius used throughout the marketing landing — not a token (between `r-lg` 12 and `r-xl` 18). Snap to a token or add one. - Wordmark letter-spacing is `0.30em` (landing) vs `0.28em` (docs/admin) and has no token — pick one value, add a `letterSpacing.wordmark` token. - Semantic badge *text* colors (`#205c47`/`#7a5814`/`#8a2828`) are darker one-offs with no token — add `semantic.*-text` tokens or reference existing ones. - Syntax-highlight colors hardcoded as hex (`#d4b985`=gold-400, `#a6b7cf`=navy-300) — switch to `var()`. One admin hex `#f6f1e7` isn't a token (closest cream-50/100) — reconcile. - Sticky-header backdrop on docs/admin (`blur(10px)`/`blur(8px)`) diverges from the contract's `blur(12px)` — align if a single header treatment is wanted.