14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13-aiui-functional-conversational-node-control-and-content-surf | 03 | execute | 1 |
|
true |
|
|
Writing backends/routstr.rs (13-13) against docs alone is how a young, actively-developed
external project turns into a debugging session inside a security-sensitive agent loop.
Purpose: a cheap, early, throwaway-safe probe that either confirms the contract or records honestly that no live provider was reachable — so 13-13 starts from a fact, and COVERAGE.md stops carrying three unconfirmed rows.
Output: an examples/ probe binary, 13-ROUTSTR-FINDINGS.md, and a rewritten COVERAGE.md.
<flagged_assumptions> None in this plan. </flagged_assumptions>
<artifacts_this_phase_produces> Symbols created by this plan:
core/archipelago/examples/routstr_probe.rs:fn main,async fn discover_providers,async fn probe_capabilities,const ROUTSTR_KIND: u16 = 38421,const DEFAULT_RELAYS- New file
.planning/phases/13-.../13-ROUTSTR-FINDINGS.md
No daemon source file, no Cargo.toml dependency, and no RPC method is added by this plan.
</artifacts_this_phase_produces>
<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/STATE.md @.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-RESEARCH.md @.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/COVERAGE.md Task 1: Probe a live Routstr provider over Nostr and HTTP core/archipelago/examples/routstr_probe.rs - `core/archipelago/src/nostr_discovery.rs` — `build_nostr_client` and how this codebase subscribes with a filter through the Tor proxy. **Reuse this shape; do not construct a second, un-Tor-aware nostr-sdk client.** - `core/archipelago/Cargo.toml` lines 83-90 — `reqwest` 0.11 (`json`,`socks`,`rustls-tls`,`stream`) and `nostr-sdk` 0.44 (`nip04`,`nip44`) are already present. An `examples/` target links the package's dependencies, so **no `Cargo.toml` change is needed and none may be made.** - `.planning/phases/13-.../13-RESEARCH.md` "Routstr chat-completions call shape" and the "Sources / Secondary (MEDIUM confidence)" block — the exact claims under test. - `.planning/phases/13-.../COVERAGE.md` — the three rows marked `INTEGRATE — UNCONFIRMED` are this probe's checklist. Create `core/archipelago/examples/routstr_probe.rs` — a standalone throwaway probe, run by hand with `cd core && cargo run --example routstr_probe`. It is an example, not a test and not a daemon path: nothing it does is shipped.discover_providers subscribes to the relays cited in RESEARCH (wss://relay.damus.io, wss://relay.nostr.band, wss://nos.lol) with a filter on kind 38421, waits up to 30 seconds, and prints every matching event verbatim: full tag list, full content, pubkey, created_at. Do not parse into a typed struct — the whole point is to see what is actually published rather than what a struct expects. Also run a second subscription with no kind filter but a #d tag filter on routstr-provider, in case the kind number in the docs has drifted; print anything it finds.
probe_capabilities takes the first discovered provider endpoint (or a --endpoint argv override so the probe is still useful when discovery finds nothing) and issues three unauthenticated GETs — /v1/models, /, and the provider's advertised info path if one appears in the event — printing status code and body for each. It must NOT send a Cashu token: this probe spends no money. If a 402 or a 401 body describes the expected payment header, print that body verbatim — that response is the single most valuable artifact this probe can capture, because it is the provider naming its own header spelling.
Print a final summary block answering exactly five questions in plain text: (1) was a live kind-38421 event observed? (2) what are its tag names and content keys? (3) what field carries the model list and what field carries the price? (4) what payment header does the provider name in a 401/402 body? (5) does /v1/models respond, and does its shape match OpenAI's?
Handle "no provider found" as a first-class outcome, not an error: print NO LIVE PROVIDER OBSERVED and exit 0. A probe that panics when the ecosystem is quiet teaches nothing.
cd core && CARGO_INCREMENTAL=0 cargo build --example routstr_probe 2>&1 | tail -5
cd core && timeout 180 cargo run --example routstr_probe 2>&1 | tail -40
cd core && git diff --exit-code -- archipelago/Cargo.toml
<acceptance_criteria>
cd core && cargo build --example routstr_probeexits 0grep -q "38421" core/archipelago/examples/routstr_probe.rsgrep -c "cashu" core/archipelago/examples/routstr_probe.rsmay be > 0 only in printed/parsing code —grep -ci 'build_payment_token\|auto_pay_token' core/archipelago/examples/routstr_probe.rsreturns 0 (the probe spends nothing)cd core && git diff --exit-code -- archipelago/Cargo.tomlexits 0 — no dependency was addedcargo run --example routstr_probeexits 0 and its output ends with a summary block that either answers all five questions or statesNO LIVE PROVIDER OBSERVED</acceptance_criteria> Anexamples/file is deletable at any time and links no shipped code. The probe builds, runs to completion, spends nothing, and prints either a live event's real shape or an explicit no-provider-observed result.
Then rewrite COVERAGE.md's matrix from those findings, not from the docs:
- Every row that the probe confirmed loses its
— UNCONFIRMEDsuffix. - Every row the probe found to differ is corrected to the observed reality.
- Every row the probe could not observe is downgraded to
OPT-OUTwith the one-line reasonnot observable — no live provider reachable on <date>, or kept asINTEGRATEonly if 13-13's first task is changed to acheckpoint:decision. Say which, explicitly, in the## Gatesection. - Do not leave a row marked
INTEGRATEon confidence this plan did not obtain. An opt-out without a reason, or an integrate without evidence, is exactly the un-decided hole the coverage gate exists to close.
Update RESEARCH assumption A2's risk line in 13-ROUTSTR-FINDINGS.md (not by editing RESEARCH.md) to state whether A2 held.
Commit both files with docs(13): routstr protocol findings + coverage matrix from live probe and push per CLAUDE.md.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| dev workstation → public Nostr relays | Outbound WebSocket; relay operators see the subscription |
| dev workstation → an unknown third-party Routstr endpoint | Outbound HTTP to an endpoint discovered from an untrusted, self-published Nostr event |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-13-16 | Spoofing | A hostile actor publishes a fake kind-38421 event advertising a malicious endpoint | medium | mitigate | The probe treats every discovered endpoint as untrusted data: it only issues unauthenticated GETs, sends no token, no key and no node identity, and prints rather than parses. Provider trust selection is 13-13's problem, gated by D-05's budget cap |
| T-13-17 | Denial of Service (financial) | Probe accidentally spends ecash | low | mitigate | The probe never calls auto_pay_token/build_payment_token; asserted by an acceptance grep. No wallet code is linked into the example's call graph |
| T-13-18 | Information Disclosure | Probe leaks node identity to relays or providers | low | mitigate | Run from a dev workstation, not a node; the probe generates an ephemeral key for the subscription and sends no node-identifying header |
| T-13-19 | Tampering | Findings recorded from docs rather than observation, defeating the plan's purpose | medium | mitigate | 13-ROUTSTR-FINDINGS.md must carry verbatim probe output; the verdict vocabulary forces NOT OBSERVED rather than an optimistic CONFIRMED |
| T-13-SC | Tampering | npm/pip/cargo installs | high | mitigate | Zero packages added — asserted by git diff --exit-code -- archipelago/Cargo.toml. No install task, so no legitimacy checkpoint is required |
| </threat_model> |
<success_criteria> 13-13 can be executed against an observed protocol or an explicitly recorded absence, and COVERAGE.md is a subtraction record backed by evidence rather than by documentation. </success_criteria>
Create `.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-03-SUMMARY.md` when done