wn now wraps account listings in {"result": [{...}]} so extract_pubkey's
object + array fallbacks both missed it and ensure_identity bailed with
"could not determine \$who npub" immediately after create-identity.
Also: create-identity on a fresh box can exit non-zero with
"failed to connect to any relays" even when the account *was* created
locally. Probe --json whoami afterwards before calling that a fatal.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq
Containers / CI often block the keyutils syscalls wnd uses by default
to store secret keys (add_key returns EACCES, keyctl_search returns
ENOSYS). The integration-tests feature ships a mock keyring store, but
the stock wnd binary doesn't wire it up. Add a tiny opt-in patch.
Changes:
- headless/patches/whitenoise-mock-keyring.patch: if built with the
integration-tests feature and \$WHITENOISE_MOCK_KEYRING is set, wnd
calls Whitenoise::initialize_mock_keyring_store() before the normal
init path. Harmless on production builds.
- headless/setup.sh: apply both harness patches generically from a
list; build wn/wnd with --features cli,integration-tests; export
WHITENOISE_MOCK_KEYRING=1 alongside WHITENOISE_DISCOVERY_RELAYS when
launching each daemon.
- preflight swaps keyctl for patch in required tools — we no longer
need a real session keyring.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq
Without this, wnd exits on startup with NoRelayConnections in any
environment that can't reach wnd's baked-in public discovery relay
set (nos.lol, relay.damus.io, …). The headless harness runs entirely
on a loopback relay, so hitting the public internet is never OK.
Changes:
- headless/patches/whitenoise-discovery-env.patch: adds an env-var
override at the top of DiscoveryPlaneConfig::curated_default_relays.
When \$WHITENOISE_DISCOVERY_RELAYS is set (comma-separated) we use
that list instead of the hardcoded public one.
- headless/setup.sh: applies the patch in preflight (idempotent —
checks for a .headless-discovery-patched marker), invalidates the
previous wn/wnd build on first patch, rebuilds, and threads
\$WHITENOISE_DISCOVERY_RELAYS=\$RELAY_URL into every start_daemon
invocation.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq
nostr-rs-relay's build.rs needs protoc to compile the nauthz gRPC
definitions. Without it the build fails deep in prost-build with a
panic that's hard to read, so fail up front with a pointer to
`apt-get install protobuf-compiler`.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq
Every test now flows through one loopback nostr-rs-relay on
ws://127.0.0.1:8080 — no public relays, no egress. Addresses the
repeated 503s we saw from the sandbox and the kind:445 rejections we
hit on public relays even with internet.
Changes:
- Preflight clones https://github.com/scsibug/nostr-rs-relay into
state-headless/nostr-rs-relay and runs `cargo build --release`
(~3 min first run). Cache hits on subsequent runs.
- New start_local_relay / stop_local_relay functions render a minimal
config.toml each run (binds to 127.0.0.1, no kind blacklist, data
under state-headless/relay) and wait up to 20s for the port.
- configure_relays collapsed to a single-relay path — A/B/C all point
at \$RELAY_URL. Kept publish-lists + key-package publish as smoke
signals for the advertise path.
- Flags pared down: drop --local-relays (always local now), add
--port N for when 8080 is taken. --no-build still honoured.
- trap wires relay shutdown alongside daemon shutdown.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq
Matches the 13 scenarios in marmot-interop.sh but runs A via the new
`amy` CLI instead of prompting the human. Identities B and C still go
through `wn`/`wnd` from whitenoise-rs.
Layout:
- marmot-interop-headless.sh — top-level entrypoint
- headless/setup.sh — preflight, daemon lifecycle, identities
- headless/helpers.sh — amy wrappers + assertion helpers
- headless/tests-create.sh — tests 01..05
- headless/tests-manage.sh — tests 06, 07, 08, 11
- headless/tests-extras.sh — tests 09, 10, 12, 13
Reuses lib.sh for colours, record_result, wait_for_invite/message,
jq_group_id, and dump_daemon_diagnostics.
Keeps the interactive marmot-interop.sh intact for final UI sign-off.
https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq