How it gets hacked, and why it mostly can't · 03_qals_architecture/QALNET_SECURITY.md
Qalnet Security — Threat Model & Hardening Plan
Date: 2026-09-06 · Question: "How easy would it be for this network to be exploited and hacked — and ways around it?" Scope: qalnet-dev-1 as running today + the Phase 1-2 design. Honest, attacker-brained, with a live audit finding already fixed-by-design.
0. The honest one-paragraph answer
A private chain run by one company is smaller-surface but higher-concentration than a public chain: there's no anonymous attacker horde (RPC isn't public), but qalarc is the trust root — if our keys/machines fall, the chain falls. The design answers this in layers: Tailscale-only consensus, chain-enforced caps on every agent (the prompt-injection-drains-the-wallet class of attack dies at the Move VM), full-reserve money that can't be printed by anyone (B-QALS mint only against confirmed AUD), and anchoring to public IOTA so our private history is publicly tamper-evident. Biggest real-world risks today are operational (keys on home machines, exposed devnet ports, fork supply-chain), not cryptographic.
1. Live findings (audit of the running devnet, 2026-09-06)
| # | Finding | Severity | Status/mitigation |
|---|---|---|---|
| F1 | RPC (:9000) and faucet (:9123) bind 0.0.0.0 — reachable from LAN | Med (devnet, no real value) | Devnet only; production binds RPC to localhost/Tailscale IP, public access via an authenticated gateway. Validator gRPC correctly localhost (127.0.0.1:19200). |
| F2 | Validator + fullnode keys live in /tmp/qalnet-dev1 plaintext |
Med (devnet) | Production: keys in Stronghold/OS keystore, 0700 dirs; genesis ceremony on isolated media. |
| F3 | Client YAML contains api_mainnet.iota.cafe envs with grpc set — accidental --env mainnet would target public IOTA |
Low | Keep active_env pinned; document; CI guard later. |
| F4 | Single validator (committee-size 1) — the operator IS consensus | By design (dev) | Phase 1: 4 validators (3f+1 tolerates 1 Byzantine), consensus traffic inside Tailscale. |
| F5 | Anchors trust producer-declared metadata (hash is real, but "what it claims to be" is producer-signed only) | Accepted (v0) | v1: signature field populated with Qal ID producer keys; issuer accreditation via Hierarchies. |
2. Threat model by surface
2.1 Network & validators
- DoS / residential internet flakiness: Starfish is designed for lagging participants (that's why we chose it); the cloud validator keeps quorum alive if a home node drops. Mitigation: 4-validator committee (tolerates 1 fault), grow to 7; alerts to Signal (hub) on epoch misses.
- Validator key theft (home machines): highest-impact realistic attack — a stolen validator key on a 4-node net can't forge history alone (needs >⅓ byzantine to halt, >⅔ to attack) but can sign bad blocks/censor. Mitigations: keys never leave Stronghold-backed storage; validator machines have no interactive logins beyond ops; Tailscale ACLs segment who can reach validator ports; disk encryption.
- Eclipse/Sybil: irrelevant while p2p is Tailscale-only (peers are explicitly our machines). Becomes relevant only if we open the validator set — gate behind accreditation + stake.
- Consensus attacks (long-range, nothing-at-stake variants): N/A to BFT with checkpointed finality; anchoring checkpoint roots to public IOTA adds external light-client verification.
2.2 Money layer (the juiciest target)
- "Print B-QALS": structurally impossible — mint entry-point requires oracle-confirmed AUD deposit; supply invariant (
reserve ≥ outstanding) is assert-enforced in the mint path; monthly attestations + auto-pause on >0.5% discrepancy. The attack becomes forge a bank statement — a fraud problem with a paper trail, not a code problem. - Reserve oracle capture (3-of-5 multisig): two colluding signers can trigger
investigate(annoyance); three can false-attest — mitigations: external signer independence, reconciliation against PSP data an attacker can't rewrite, and the chain's own liability ledger (any false attestation is provably false the moment redemption exceeds reality — the system is designed to fail loud, not silent). - Hold/settle games (provider overcharging): actual clamped to hold; telemetry cross-checked (wall clock, benchmarks); sampling verification; slashing. Buyer exposure is bounded by the hold they placed — the petrol-card property.
- Replay/double-spend: object model with versioned UIDs — replay is a non-issue at the VM level (each tx consumes unique objects).
2.3 Agent layer (the NEW attack class everyone's getting wrong in 2026)
- Prompt injection → wallet drain: the headline attack of the agent economy. Ours is bounded structurally: epoch caps in
qal_credit(a hijacked agent physically cannot exceed its allowance), per-job Holds (it can't commit future funds), revocation flips a status bit (~2s network-wide). A fully-pwned agent's worst day = its daily cap, with a complete signed audit trail. - Agent key theft: blast radius = cap + revocation; keys are delegated (governance DID is ops-controlled) — agents never custody master keys.
- Malicious tool/MCP server: tools sit behind the gateway (holds + caps apply per call); capability VCs name exactly which tools an agent may invoke.
2.4 Contract layer (Move)
- Move's design (linear types, no dynamic dispatch, resource safety) kills entire bug classes (reentrancy, phantom assets) that plague Solidity. Remaining risks: our logic bugs (bad cap math, hold mismatches) → mitigated by property tests (8/8 today, growing), the linter suite (already caught patterns during publish), and a paid external audit before any real value — budgeted in the business review.
- Genesis misconfig: fixed supply, no admin "mint" capability in
qal_*packages by construction; upgrade authority: packages initially immutable (nocapobjects exposed) until governance needs it.
2.5 Supply chain (the underrated one)
We run a ~200-crate fork of third-party code. Real risks: upstream compromise, malicious dep injection via a future cargo add, stale CVEs. Mitigations: Cargo.lock committed; monthly upstream rebase cadence (implementation plan); cargo audit/dependabot on the fork repo; builds reproducible-ish on one machine; no code runs from repos/ (reference only) — only qalnet/ is built.
2.6 Data layer (accountability pipeline)
- Hash collisions: sha256 — no practical threat; anchor stores exactly 32 bytes.
- Payload availability rot (URI dies): the real long-term risk — mitigations: MinIO replication across fleet + periodic re-verification (
qalpipe verify-dir), anchor stores content hash so any surviving copy re-validates. - Metadata lying: anchor says what producer claims — v0 accepted, v1 producer signatures + accredited issuers (F5).
- Registry loss: verification reads the CHAIN (registry is only an object-id index) — survives the registry being destroyed.
2.7 Physical/ops
Home machines: power/net loss (Starfish tolerates; DR drills monthly), disk death (snapshots + snapper on superlocal; chain DB rebuildable from genesis.blob), theft (disk encryption, keys in Stronghold, revocation drills). The hub's Signal alerts are the incident channel.
3. "How easy is it to hack?" — scored
| Attack | Difficulty today | After Phase 1 (4 validators, hardened) |
|---|---|---|
| Steal a user's B-QALS | needs their machine+keys | same (no chain path exists) |
| Drain an agent | capped by epoch allowance; revoked in 2s | same, smaller caps, on-chain |
| Forge a receipt/anchor | would need to rewrite chain history | + publicly falsifiable (IOTA anchoring) |
| Print money | no path exists (paper phase: admin of loopd) | on-chain: needs reserve oracle majority + bank fraud |
| Halt the chain | DDoS superlocal's link (devnet: trivial; cost: nothing at stake) | needs 2 of 4 validators offline |
| Corrupt history | 1-of-1 validator today (devnet!) | >⅓ committee Byzantine + defeats anchoring checks |
Devnet is soft on purpose — it holds no value. The table above is why Phase 1 (committee + hardening) precedes any real money.
4. Hardening backlog (ordered)
- Bind all devnet services to 127.0.0.1/Tailscale IP (config, not firewall surgery).
- Keys out of /tmp → Stronghold + 0700 (script it).
- 4-validator committee across Fleet + 1 cloud VM; DR drill (kill one, watch Starfish heal).
cargo audit+ lockfile commit on qalnet; monthly upstream rebase.- Property-test expansion for qal_credit edge cases (concurrent holds, epoch boundary).
- Anchor
authorized_keys/ACL hashes (the SSH/Tailscale integration) — dogfooding F5's fix. - External Move audit before Phase-2 value (US$30-80k, budgeted).
- Public-IOTA checkpoint anchoring cron (external tamper-evidence).
- Revocation drill scheduled monthly (hub → Signal).
- Incident runbook (who, what, Signal channel, kill switches).
Philosophy: we're not trying to be unattackable (nobody is); we're making every attack bounded, loud, and provable — caps bound it, events/alerts announce it, receipts and anchors prove it. That's a security posture you can sell.