Status: canonical security model (Phase 2, October 2026).
Suban handles market data, chain access and value-movement code. This document states the threat model, the controls that exist, and the gaps that are still open. It is deliberately conservative: a capability is not "secure" because it compiles.
Suban is currently testnet-first. No mainnet capital product should be considered safe until the gates in
ROADMAP.mdand the audit requirements inAUDIT-PROGRAM.mdare met.
Do not open a public issue for a vulnerability that could move funds, leak keys or forge oracle data.
Use GitHub's private Security Advisories feature on the affected repository
(Pi-Defi-world/Suban, Pi-Defi-world/suban-rpc,
Pi-Defi-world/Suban-api, Pi-Defi-world/suban-controller). Include:
- affected repository, commit and deployment;
- reproduction steps or a proof of concept;
- impact (funds, oracle integrity, availability, data);
- whether the issue is already public.
If the advisory feature is unavailable, contact the repository maintainers through the private channel listed in the repository profile rather than a public issue. Do not test against third-party funds or accounts.
| Asset | Objective |
|---|---|
| Oracle prices | Integrity: no fabricated, stale or single-source price reaches consumers or the chain |
| Chain data | Correctness and freshness; a derived view must be replayable |
| Bridge value | No mint without authorized burn and required signatures |
| Privileged keys | Confidentiality, least privilege, rotation, isolated signing |
| Service availability | Public read APIs stay up; control plane stays private |
| Historical data | Integrity and retention; observations are append-only |
| User/operator trust | No secret in source, logs, images or public docs |
Internet ──► public read APIs (controller, API, RPC, indexer)
│ rate limits, validation, no writes
▼
control plane (controller admin, oracle signer, relayer)
│ shared secret / signing keys / isolated network
▼
Pi Horizon / Soroban RPC / Arc RPC
- Public reads and privileged operations are logically separated by authentication and must be separated by network policy in deployment.
- The controller admin key, oracle admin secret and relayer keys never belong in a frontend, public API process or container image.
- The relayer is a worker, not a public API. Its only listener is the metrics/health port.
| Threat | Impact | Controls | Status |
|---|---|---|---|
| Malicious or compromised price source | Wrong oracle price | Multiple sources, weights, outlier filter, freshness, confidence, per-asset breaker | Implemented |
| Source outage or stale data | Silent degradation | Per-source age/status, trust state, 503 for unconfigured assets |
Implemented |
| Single bad observation | Bad on-chain price | Only HEALTHY observations are pushed; confidence threshold; breaker |
Implemented |
| Forged bridge signer list | Unauthorized mint | Every signer must be configured, unique and require_auth(); tests cover non-signer/duplicate/replay |
Implemented |
| Arc→Stellar mint abuse | Unauthorized mint | Path disabled until signer authorization entries exist | Mitigated by disablement |
| Unauthenticated control route | Unauthorized price/breaker change | CONTROLLER_ADMIN_API_KEY, constant-time compare, per-IP limit, 503 if unset |
Implemented |
| Leaked secrets in git | Key compromise | Secrets removed from tree, gitleaks working-tree gate, rotation runbook | Partial: rotation/history purge pending |
| Key compromise | Funds/oracle control | Isolated env, rotation, least privilege; multisig/HSM is a target | Partial |
| RPC/Horizon abuse | DoS, quota exhaustion | Rate limits, caching, upstream timeouts | Partial |
| Supply-chain compromise | Malicious dependency/build | Lockfiles, pinned ed25519-dalek, --locked, npm ci, CI build |
Partial: production Node audits block high/critical; Rust audit is enabled; dev-only findings are triaged separately |
| Indexer data loss/corruption | Wrong analytics | SQLite checkpoints, replayable derived view, backups | Partial: backup drill not documented |
| Public data API key abuse | Quota exhaustion, cost/DoS | Per-key minute/day quotas, hashed keys, 429 + Retry-After, usage endpoint |
Implemented |
| Webhook spoofing or replay | False event delivery | HMAC-SHA256 signature over raw body, stable Idempotency-Key, retries |
Implemented; secret storage is DB-protected |
| Stale indexed data presented as live | Wrong analytics/decisions | freshness on every response, /health 503 when stale, lag alerts |
Implemented |
| Public data admin route exposed | Contract/webhook tampering | INDEXER_ADMIN_API_KEY, constant-time compare, 503 if unset, private network |
Implemented |
| Oracle push failure | Stale on-chain price | Push only after success; failed push does not move breaker reference; metrics | Implemented |
| Pause-registry not wired | Inability to stop contracts | Pause registry exists but is not called by value-moving contracts | Open gap |
| Contract bug | Loss of funds | Tests, workspace CI, audit program; Phase 4.7 prep package | Open gap: no independent audit |
- Control routes require
CONTROLLER_ADMIN_API_KEYviax-api-keyorAuthorization: Bearer; comparison is constant-time; unset key returns503, never falls open; per-IP rate limiting. - Oracle publication is opt-in (
ORACLE_PUSH_ENABLED=falseby default) and requiresORACLE_CONTRACT_ID,ORACLE_ASSET*,ORACLE_ADMIN_SECRET. - Only
HEALTHYobservations are published. Confidence is sent as a 0–100 integer, matching the contract. - Every price carries
schema_version,method_version,age_ms,is_fresh, source provenance and a trust snapshot. - Per-asset circuit breakers prevent a single asset's failure from affecting another.
- SQLite persistence is fail-closed at startup if the store cannot be opened.
mint_pusddoes not trust a caller-supplied signature list. Every listed signer must be configured, unique and pass Sorobanrequire_auth().- Threshold, non-signer, duplicate, replay and missing-authorization cases are covered by tests.
- Arc→Stellar mint is explicitly disabled until signer authorization entries are implemented; the relayer cannot silently enable it.
- The relayer's only network listener is
/metrics+/healthon9464.
- Public data routes support per-key quotas; keys are stored as SHA-256 hashes,
usage is persisted per key, and exhausted keys receive
429withRetry-After: 60. - Admin routes require
INDEXER_ADMIN_API_KEY; unset returns503, never falls open. Admin routes are intended for a private network. - Indexed queries read SQLite only. They never proxy live Horizon/RPC calls, so
a
404means "not indexed", not "not on chain". - Every data response carries
freshness;/healthreturns503when stale. - Webhooks use a durable outbox, unique
(subscription, event)delivery, HMAC-SHA256 signatures, a stable idempotency key and bounded exponential backoff. Secret values are omitted from list responses. - The SQLite database contains webhook signing secrets and API-key usage; treat the file as a secret and restrict filesystem access.
- GitHub Actions in all four repositories: Rust workspace build/test, Go build/vet/test with a gated preflight package, SDK typecheck/test, service builds, secret scanning.
- Gitleaks scans the working tree as a blocking check and full history as a report-only check.
suban-rpcpinsed25519-dalek = "=2.2.0", commitsCargo.lockand builds with--lockedto prevent an unreviewed dependency graph.- Production Node dependencies are audited on every CI run (
npm audit --omit=dev --audit-level=high,pnpm audit --prod --audit-level=high), and Rust dependencies userustsec/audit-check. Full/dev trees are reported, not silently ignored. The controller, API, SDK, indexer, relayer and escrow viewer are clean at production level; the controller was upgraded to@stellar/stellar-sdk@17.2.1with patchedaxios,proxy-addr,form-dataandpath-to-regexp. - Controller uses Node 22+ for
better-sqlite3prebuilt compatibility.
- Caddy/Cloudflare provide TLS at the edge; internal container traffic is HTTP and must remain on private networks.
- Incident severity, contacts and procedures:
INCIDENT-RUNBOOK.md. - Secret rotation and history purge:
SECRET-ROTATION.md. - Metrics and dashboards:
OBSERVABILITY.md.
| Gap | Risk | Required action | Owner |
|---|---|---|---|
| Secret rotation not executed | Compromised keys remain valid | Rotate per SECRET-ROTATION.md; revoke old keys |
Security lead / owners |
| Git history not purged | Secrets remain in clones/forks | Approve destructive rewrite, bundle large-binary removal, flip history scan to blocking | Repository owner |
| No independent audit | Contract bugs may be unknown | Send PHASE4-AUDIT-PREP.md to an auditor; do not deploy mainnet capital |
Contracts owner |
| Pause registry unwired | Cannot globally pause value-moving contracts | Wire pause checks into contracts that need it; test | Contracts owner |
| Arc→Stellar mint disabled | Bridge direction unavailable | Implement signer authorization entries and tests | Bridge owner |
| Dependency audit pending | Known-vulnerable dependency may ship | Production Node audits and Rust audit are in CI; triage report-only full/dev findings | Platform owner |
| Data-platform public deployment not provisioned | No live public endpoint/backfill | DNS/TLS, key policy, private metrics, production backfill and load test | SRE owner |
| Webhook signing secrets in SQLite | DB read exposes webhook secrets | Restrict file permissions, backup encryption, rotate per subscription | Indexing owner |
suban-rpc preflight protocol mismatch |
Contract-state preflight tests not in the gate | Upgrade host to protocol 25 or keep the documented compatibility limit | RPC owner |
| Single-sig relayer keys | One key compromise moves funds | Multisig/HSM design before mainnet value | Bridge owner |
| No backup/restore drill | Data loss after disk failure | Document and test SQLite backups | SRE owner |
| Controller control plane in same process | Larger attack surface | Network isolation now; separate control listener/process later | Controller owner |
| oUSD feed unconfigured | Cannot price oUSD | Configure a reviewed OUSD_PRICE_URL and source validation |
Oracle owner |
Accepted for now because Suban is testnet-first: internal HTTP between containers, single-region deployment, and log-only history scanning. These are not acceptable for mainnet capital.
- One key, one purpose. Never reuse an oracle admin key as a relayer key.
- Keys live in the deployment secret store or an isolated
.envwith strict permissions; never in git, images, CI logs or chat. - The controller admin secret is not an oracle signing secret and must not be distributed to API consumers.
- Rotate on personnel change, suspected exposure or incident. Rotation is not complete until the old key is revoked and monitored.
- Prefer hardware-backed or multisig signing for any mainnet value movement.
- CI must never print secrets; use masked repository secrets.
Before any mainnet capital:
- Independent audit with published scope and remediation.
- All value-moving contracts in the root workspace and CI.
- Pause/stop path wired and tested.
- Authorization tests for every privileged entry point.
- Replay/idempotency tests for bridge and settlement paths.
- Upgrade/admin keys behind multisig or timelock.
- Deterministic risk formulas with test vectors (Phase 3).
- A staged rollout with caps and monitoring.
See AUDIT-PROGRAM.md for the audit scope and PRODUCTION-ROADMAP.md for the
bridge production requirements.
- No secret in the diff; gitleaks passes.
- New privileged route has auth, rate limit, tests and a
503fail-closed path when unconfigured. - New external data source has timeout, freshness, error handling and a metric.
- New financial number has source, timestamp, freshness, confidence and method version.
- New contract entry point has authorization and replay tests.
- New dependency is pinned/locked and reviewed.
- Logs redact keys, signatures and full request bodies containing secrets.
- Docs updated in the same change as behavior.
Use INCIDENT-RUNBOOK.md. Severity definitions:
| Severity | Meaning | Target response |
|---|---|---|
| P0 — Critical | Active fund loss, key compromise, forged oracle/bridge mint | Immediate; page owners |
| P1 — High | Service down, control-plane exposure, data corruption | < 1 hour |
| P2 — Medium | Degraded price quality, indexer lag, partial outage | < 4 hours |
| P3 — Low | Minor bug or cosmetic issue | < 24 hours |
During an incident: stop the bleeding (disable push/relayer/pause), preserve evidence, rotate exposed keys, notify owners, then write a blameless postmortem with concrete follow-ups.
- ARCHITECTURE.md — trust boundaries and data flows.
- DATA-PLATFORM.md — indexed data security/freshness contract.
- SECRET-ROTATION.md — rotation and history purge.
- DEFERRED-OWNER-ACTIONS.md — owner-blocked follow-ups.
- INCIDENT-RUNBOOK.md — procedures and severity.
- AUDIT-PROGRAM.md — audit scope.
- PRODUCTION-ROADMAP.md — bridge production gates.
- OWNERSHIP.md — accountable owners.
- PLAN-EXECUTION.md — open defects and progress.