QALS Wiki · the qalarc network⌂ qalarc.com/projects/qals
generated 2026-09-22 · qalcode autonomous research

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, owner-set ledger-enforced caps on any agent (optional, uncapped by default — the prompt-injection-drains-the-wallet class of attack dies at a set cap), full-reserve money that can't be printed past the cap (B-QALS issued only against confirmed AUD; supply pre-endowed in the issuer wallet qals:issuer — cap gate fail-closed in loopd, on-chain twin written + tested), 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

2.2 Money layer (the juiciest target)

2.3 Agent layer (the NEW attack class everyone's getting wrong in 2026)

2.4 Contract layer (Move)

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)

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)

  1. Bind all devnet services to 127.0.0.1/Tailscale IP (config, not firewall surgery).
  2. Keys out of /tmp → Stronghold + 0700 (script it).
  3. 4-validator committee across Fleet + 1 cloud VM; DR drill (kill one, watch Starfish heal).
  4. cargo audit + lockfile commit on qalnet; monthly upstream rebase.
  5. Property-test expansion for qal_credit edge cases (concurrent holds, epoch boundary).
  6. Anchor authorized_keys/ACL hashes (the SSH/Tailscale integration) — dogfooding F5's fix.
  7. External Move audit before Phase-2 value (US$30-80k, budgeted).
  8. Public-IOTA checkpoint anchoring cron (external tamper-evidence).
  9. Revocation drill scheduled monthly (hub → Signal).
  10. 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.

Findings addendum (2026-09-10, from the multi-relay test campaign — 53/53 green)

  1. init aborts if any relay in --relays is dead (qalchat.py:975 unguarded http_json) — one down relay blocks identity creation pre-loopd-claim. Fix queued: per-relay warning + continue.
  2. import-env "no relay contact" guarantee holds only for pinned senders — first-contact imports need a keyserver (qalchat.py:2039/497). Documented in ferry usage.
  3. Envelope-id dedupe is per-server (ids minted per-POST, qalchat.py:2334): cross-relay replicas surface as chain-gap noise (exit-3), not silent dupes — bounded, loud, acceptable; full fix = client-minted ids.
  4. Tailscale DERP flaps on the fleet path (20s+ latency spikes) — all cross-machine tests now warm up tailscale ping; monitors should alert on DERP-sustained paths.
  5. Relay #2 confirmed live through the full matrix: failover, isolation, 2MiB file transfer (sha256 ✓, anchored), offline ferry with replay rejection.
  6. Fleet availability flap observed live during verification: relay #2 answered, timed out, answered again within minutes (systemd Restart=always on our side — the machine/network itself wobbles). Verified states: agent run 53/53 → my run 27+1fail (DERP timeout) → retry 48/49 (repost window missed during flap). Action: heartbeat→Signal alerting (SECURE_OPS weekly item) promoted to NOW; consider a third relay for quorum-of-mailboxes.

Red-team campaign 2026-09-10 — findings & fixes

A dedicated attacker agent ran the full attack book (findings-only, all PoCs reversed). 4 CRITICAL · 5 HIGH · 8 MEDIUM · 4 LOW — full report: 11_system_audit/REDTEAM_FINDINGS_2026-09-10.md (PoCs preserved in redteam_20260910_poc/).

CRITICALs — all fixed same-day (verified live): - C1 unauth topup → admin-key gate (QALLOOPD_ADMIN_KEY; paper-mode warns loudly) - C2 untracked registration balances → opening balances now count against the 500M program cap - C3 negative settle = infinite money (settle -500000 credited +500k) → hard clamp max(0, …); verified: exploit attempt settles 0 - C4 holds never escrowed (expiry-release was phantom credit) → holds now debit at placement (petrol-card, matching qal_credit on-chain); settle gained to_agent_id (escrow pays beneficiary directly — the on-chain semantics, in the paper rail)

Still open (queued, from the report): bind chain RPC to 127.0.0.1 at next regenesis; relay pubkey registration needs signed proof (fleet-relay TOFU hijack); marketplace offer signing (seller ed25519); qalpipe verify should bind the registry entry to the original object_id+timestamp (registry-rewrite hardening). HIGHs and below: see the report — each has a concrete patch sketch.

WAVE-25 addendum (2026-09-11)