Skip to content

fix(cli): say the right thing for webhooks, setup codes, unreachable servers and keychains - #2258

Merged
aicayzer merged 17 commits into
mainfrom
fix/099-cli-words
Oct 21, 2026
Merged

aicayzer merged 17 commits into
mainfrom
fix/099-cli-words

Conversation

@aicayzer

@aicayzer aicayzer commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Six small points in the command line's words and exit codes that a person meets on a first day: a webhook's deliveries said nothing useful, a refused redelivery gave no reason, a new webhook's secret was buried in a description, a wrong setup code exited like a bad key, an unreachable server did not say which one, and a locked keychain was reported as "no server named".

Changes

  • webhooks deliveries shows each delivery's status, event, attempts and the receiver's answer (core/marfa-cli/src/output.rs).
  • The server refuses a redelivery with 409 conflict and details.reason set to one of webhook_inactive, credential_unavailable, delivery_not_failed, delivery_expired or delivery_not_retained, in words (packages/server/src/routes/webhooks.ts; contract events/redeliver-conflict-reason).
  • webhooks create prints the signing secret alone on the first line, as keys create does, then a description that never repeats it. A --secret the person gave is not echoed. --json still returns secret.
  • A refused setup code has its own error code, setup_proof_invalid (status 401), and the CLI exits 1 for it instead of 5 (packages/shared/src/errors.ts, auth/instance-claim.ts, routes/setup.ts, core/marfa-cli/src/error.rs; contract device/claim-refused-proof-exit, the instance-claim statements, the errors table).
  • An unreachable server is named by its scheme, host and port alone, as request to <origin> failed: <cause>, and the network error no longer starts its message with its own code (core/marfa-core/src/http.rs, error.rs; contract device/command-unreachable-origin, device/command-unreachable-code-once).
  • A keychain that cannot be read is reported as no_keychain (exit 4) with the keychain's own reason, not as no_server (core/marfa-cli/src/remote/mod.rs; contract device/command-keychain-unreadable). With the keychain off or empty, no_server (exit 2) stays. folders pull, folders restore and device blobs get still run on held bytes alone when the keychain cannot be read, as over SSH on a Mac with no default keychain (Named::session_if_named).
  • The webhook history page's notice for a refused retry shows the server's own reason from error.message, and falls back to the general sentence only when there is none (packages/server/src/pages/client/webhooks.ts).
  • openapi.json, the generated client schema, the errors table and COMMANDS.md are regenerated.

Testing

  • Each change except the secret layout has a test seen failing first, then passing: the delivery cells, each redelivery reason (new webhook-redelivery-refusals.test.ts, with a witness that a failed delivery is accepted), the setup code exit (Rust unit test and device/contract.test.ts with the CLI change removed, exit 5), the error code (instance-claim.test.ts, four tests failing with the source reverted), the unreachable origin (Rust test) and the keychain (device/contract.test.ts, exit 2 before, 4 after, with a witness for the keychain-off case that still exits 2).
  • Ran: cargo fmt --check, cargo clippy --all-targets -- -D warnings (marfa-cli, marfa-core, the Swift binding), cargo test -p marfa-cli --bin marfa (300), cargo nextest run -p marfa-core (859) and the Swift binding (50), pnpm format:check, pnpm lint, pnpm typecheck, and pnpm test:changed (all passed, including the spec-form, OpenAPI style and settings census checks).
  • Ran live against a throwaway server and the built binary: the delivery listing, a refused redelivery with the delivery_not_failed and webhook_inactive reasons, the webhooks create output and --json, a wrong setup code against a real unclaimed server (exit 1, server.code setup_proof_invalid), an unreachable address and a DNS failure, and a locked, missing and unreadable scratch keychain (old binary: "no server named", exit 2; new: "the keychain cannot be used: ", exit 4). No login keychain was used.
  • Review fixes: a new device/contract.test.ts test runs folders pull and whoami with MARFA_KEYCHAIN naming a missing file; it failed first (pull exit 4), then passed (pull exit 0, whoami exit 4, and folders pull --url exit 5 no_credential). The retry notice change has no browser test and was not run in a browser. cargo fmt --check had failed on drain.rs from an earlier commit on this branch, so that is formatted.
  • Not tested: the webhooks create layout has unit tests written with the change, not seen failing first; the redelivery reasons delivery_expired, delivery_not_retained and credential_unavailable are covered by server tests but not run through the CLI; cli/exits.test.ts and the webhook conformance files were not run against a booted server; the keychain change is untested on Linux; the literal doubled network: network: was not reproduced (the repeat came from surfaces printing the code beside a message that began with it); the host on which the keychain symptom was reported (Atlas) was not reproduced on, only a locked or unreadable keychain on this Mac; the Swift package in marfa-swift was not checked for the old network: text.

Notes for reviewers

  • Review depth: standard.
  • Not changed on purpose: decoding: and store: error prefixes repeat their code the same way; auth::resolve_credential and login.rs still treat an unreadable keychain as "no credential"; the drain and blob wrappers keep "the server could not be reached" because conformance tests match that text.
  • A command that reads the current server only if one is named (folders pull, folders restore, device blob read) runs on held bytes when the keychain cannot be read; every other command reports no_keychain with exit 4.
  • The stderr envelope for a refused setup code keeps error.code unauthorized (the CLI derives it from the status) and carries the server's code in error.server.code; the exit is 1.
  • For the release docs pass: the exit code table (Exit::meaning for refused and local), the webhook CLI output (deliveries listing, the creation output), and the secret handling (secret first line, never repeated, --secret not echoed).
  • Overlaps: fix(core): finish a push after a key change, keep waits at hand-over #2255 touches core/marfa-core/src/drain.rs and fix(server): make the owner-facing words true and consistent #2257 and fix(cli): keep item content off the private socket and say so #2256 touch error.rs, remote/mod.rs, device.md and COMMANDS.md; this branch is rebased on current main and the edits are in different places, but whichever merges second regenerates COMMANDS.md.

Checklist

  • The title is a scoped Conventional Commit.
  • New behavior has a test that was seen failing first, or the reason it has none is stated above.
  • Docs, comments and the contract still describe what the code does.

@aicayzer aicayzer left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: ready to merge

Summary. The CLI's words and exit codes now say what happened: delivery status in webhooks deliveries, the reason a redelivery is refused, the webhook secret printed once, exit 1 for a wrong setup code, the server's origin in a network error, and an unreadable keychain named as such; safe to merge.

First draft: 3/5. 1 blocking issue and 3 smaller ones found; the blocking one and one smaller one fixed in a655b007b and 9272e09f6, prose in 8f9ff33dc.

Found.

  • Blocking: naming an unreadable keychain made folders pull, folders restore and device blobs get stop instead of running offline, which is the normal state over SSH on a Mac. Fixed: those three treat an unreadable keychain as nothing kept; every other command still exits 4. The statement and a conformance test follow it.
  • The console's retry notice guessed at the reason the server now states. Fixed: it shows the server's message.
  • delivery_not_retained is reachable only through a damaged row. Kept as a last fallback.
  • A stream that breaks after it opens does not name the server. Left: the path, query and key never appear, and the origin is named where the connection is made.

Checked. setup_proof_invalid is thrown wherever a setup proof was refused, and matched by the setup pages; redelivery reasons are checked only after the caller can see the webhook, so nothing crosses owners; #2249's access rules are unchanged; network errors carry the origin only.

@aicayzer
aicayzer marked this pull request as ready for review October 21, 2026 10:11
@aicayzer
aicayzer enabled auto-merge (squash) October 21, 2026 10:11
Linux has no keychain file, so nothing can be kept there; keeping or removing an entry is still refused with the reason.
Off macOS a named keychain file reads as holding nothing, so no server named is the true answer there. The Node watch test now checks the unreachable reason names the server.
Comment thread core/bindings/node/test/watch.test.mjs Fixed
@aicayzer
aicayzer merged commit 3a53c28 into main Oct 21, 2026
35 checks passed
@aicayzer
aicayzer deleted the fix/099-cli-words branch October 21, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants