D2 — Transaction security scanner
Statement of Work: Deliverable 2, §4.1 · Target: Week 2 (14–20 Sep) · Status: Complete — recording pending
"A scanner that simulates any Stellar transaction via RPC, decodes the
SorobanAuthorizationEntrytree, and screens counterparties against the on-chain registry. Committed coverage: full structured effect-decoding and plain-language explanation for payment operations and token-interface calls (SEP-41 / SAC) — the operations most users actually sign." — SOW §4.1
Evidence
| SOW §6.1 evidence | Where | What it shows | Status |
|---|---|---|---|
| Public MIT-licensed repository | LICENSE · packages/lantern-scanner/LICENSE |
MIT at the repository root and inside the package; "license": "MIT" in both package.json files |
Present |
| Scanner test suite green in CI, offline | Tests workflow | 748 passed, 1 skipped, 67 files, no network — every RPC answer comes from a recorded fixture; no CI secret exists | Present |
| 90-second recording: a payment and a token-interface call decoded into effects + summary + verdict, plus an unverified contract handled safely | TODO: link — pending | Shot list and read-aloud script are written; the recording has not been made | Pending |
| QA sign-off against the manual plan | docs/qa/d2-transaction-scanner-test-plan.md |
Complete: a ten-section plan with a results table, merged; the author's full dry run passes every runnable case including the three hard blockers. Not complete: an independent tester's sign-off in the results table | In progress |
| Evidence decks + raw captures | docs/evidence/d2/ |
Proof of Deliverables (9 slides) and Technical Documentation & Demo Evidence (6 slides); every proof slide's terminal output is committed alongside so a slide can be diffed against its source | Present |
What a user can do when this is done
Before signing anything, a user (or a wallet, a dApp, a command line) can hand the scanner a transaction and get back: exactly what it does — what leaves, what arrives, who receives, including money moved by a contract calling another contract — whether any recipient has been reported on the Deliverable 1 registry, a risk level with reasons that comes from deterministic rules, and one plain-English sentence. When the scanner cannot read or simulate the transaction, cannot reach the registry, or does not know what a contract does, it says so and raises the risk instead of guessing.
Traceability
| SOW clause | Change | Evidence | Status |
|---|---|---|---|
| Open-source, MIT-licensed package | addfea1 — @lantern/scanner extracted with its own LICENSE and README |
Repository | Evidenced |
| A pipeline whose verdict cannot be moved by the explainer | 57ff777 — six stage signatures; verdict and effects deep-frozen before stage 6; type-level test |
Proof deck slide 9; tests/scanner-explain.test.ts |
Evidenced |
| §4.1 — simulate via RPC | d8ffe18 — Ingest: timeout + backoff, fail-closed on undecodable / transport / timeout / malformed / reverted / archived |
Proof deck slide 8 (fails closed); QA plan section 7 | Evidenced |
| §4.1 — decode the authorization tree | 3bd3123 — every entry, every nested sub-invocation, with depth |
Proof deck slide 5 | Evidenced |
| §4.1 — structured effects for payment operations | f9048d0, 6e0f2c5 — payment, createAccount, path payments (min/max bounds), accountMerge (entire balance), per-address aggregate |
Proof deck slide 3; QA plan section 3 | Evidenced |
| §4.1 — structured effects for SEP-41 / SAC token calls | 3f09105 — transfer, approve (allowance + expiry, unlimited flagged), burn, mint, clawback; amounts scaled by resolved decimals or shown raw with "decimals unknown" |
Proof deck slide 4; QA plan section 4 | Evidenced |
| §4.1 — an unverified contract handled safely | 57f34cc — structured label unverified — semantics unknown, decoded args shown, no name-based guessing, risk raised |
Proof deck slide 6; QA plan section 5 | Evidenced |
| §4.1 — screen counterparties against the registry | c30f30a — every address, three outcomes, unknown never folded into clean, TTL cache |
Proof deck slide 7 with the explorer row; QA plan section 6 | Evidenced |
| A deterministic risk verdict | faa3829 — pure rules; signal trail with provenance; effects shown, terms not judged on every verdict |
Proof deck slides 3–8 | Evidenced |
| §4.1 — plain-language explanation | e76700d — one hosted model (claude-haiku-4-5-20251001), structured facts only, 4 s deadline, sanitised, contradiction-guarded, rules-based fallback |
Proof deck slide 9; QA plan section 8 | Evidenced |
| No key ships in a client | 418d4c1 — the Lantern API proxy (POST /v1/explain), rate-limited and capped; 3addf40 — AI flag OFF in release builds |
Service /healthz; release workflow |
Evidenced |
| §6.1 — manual QA plan and a way to run the scanner by hand | 73d5b34 — npm run scan harness (online, offline, --no-ai, hostile-stub mode) + the plan with real fixture names |
Test plan; dry run | In progress |
| §6.1 — 90-second recording | Runbook written | — | In progress |
Scope notes
- Committed coverage is payments and the token interface. Anything else is decoded as far as the bytes allow and labelled unverified — semantics unknown; the scanner does not attempt DeFi-protocol semantics in D2.
- The scanner never signs and never submits. Its only network calls are two read-only RPC methods (
simulateTransaction,getLedgerEntries) and, when configured, the explainer. - The verdict is about what a transaction does, not whether it is a good deal (§3.9) — every verdict carries that scope statement as a constant.
Implementation choices
- The AI layer arrived last, on purpose. The deterministic core was built and tested to stand on its own before a model was attached to it; the SOW explicitly allows the verdict to stand without the sentence.
- Option 2 for the model key — ship none — then option 1, a proxy, before any public surface. A browser extension cannot keep a secret, so the release builds carry no key and no endpoint; the Lantern API holds the key and serves the sentence with a per-IP window and a daily cap.
- Offline by construction. Nineteen recorded fixtures (real testnet XDR and the raw RPC bodies) answer every network call in tests; a QA harness
--offlinemode uses the same recordings and fails closed on anything unrecorded. - Three fixtures were added for the QA plan (payment to the flagged address; two recipients with one flagged; the flagged payment with a prompt-injection memo) so a tester can exercise screening and the injection case without building transactions.
