CLI and protocol reference

Endpoints, metrics, error catalog, policy schema, and storage tables in one place.

13.1 Endpoints

In this chapter Know every route under /__sibuna/ and what each returns.
EndpointMethodFunction
/__sibuna/challengeGETThe interstitial page
/__sibuna/challenge.json?need=&path=GETIssues a stateless challenge: the requirement ticket from the challenged response decides it, otherwise the reported URL is evaluated (414 above 8 KiB). Returns {id, algorithm, difficulty, challenges, expires_at}
/__sibuna/verifyPOSTAccepts {"challenge_id", "nonce"} or {"challenge_id", "proof"}; 200 with Set-Cookie, or 400 with a diagnostic
/__sibuna/wasm/sibuna-pow.wasmGETThe 8,831-byte solver module, cacheable
/__sibuna/worker.jsGETThe Web Worker with WASM and JavaScript provers, cacheable
/__sibuna/honeypotGETBans the caller for --ban-seconds and records an incident; 403
/__sibuna/healthGETJSON liveness status with engine name, version, proxy mode and proof algorithm
/__sibuna/metricsGETPrometheus text format

Challenge responses carry X-Sibuna-Status: CHALLENGE; admitted requests carry X-Sibuna-Status: PASS and X-Sibuna-Rule upstream (and X-Sibuna-Rule-Hash in forward-auth replies).

The separate opt-in management listener serves /console/. Initialize its administrator locally with sibuna init-admin, then replace the temporary password on first sign-in. /console/api/stats and /console/ws require an authenticated session. The WebSocket multiplexes statistics, events, nodes, policy, challenges and audit with bounded snapshots, deltas and gap recovery. /console/stream retains the earlier statistics-only protocol. Origin error counters are not observed in forward-auth mode. Part IX documents the console's workflows, HTTPS deployment and current SID 0007 limits.

13.2 Metrics

Counters exposed as sibuna_<name>_total in Prometheus text format:

CounterIncremented when
requestsA request head was parsed
allowed, denied, challengedA policy decision was made (admitted, refused, challenge issued or reissued)
challenges_issued/__sibuna/challenge.json minted a challenge record
solutions_accepted, solutions_rejected/__sibuna/verify accepted or rejected a proof
rate_limitedGCRA refused a request (429)
bannedA banned address was refused, or the honeypot banned one
proxied, upstream_errorsA request was relayed to the origin, or the origin failed (502)
parse_errorsA malformed head was refused (400)
overloadedA connection beyond --max-connections was answered 503
incidents_persisted, incident_batchesRecords and transactions whose commit the storage thread confirmed
incidents_dropped, incident_write_failuresQueue pushes rejected because the ring was full, and failed commit attempts

A retry can increment incident_write_failures without losing records, because the pending batch is retained. Monitor increments over an interval; totals alone are not a queue depth.

13.3 Status Codes

CodeWhen
200Admitted (forward-auth), interstitial (HTML navigation needing a challenge), internal routes
302Not used by the current protocol; verification answers 200 and the page reloads
400Malformed request, or a rejected solution with an Elm-style diagnostic
401Challenge required for a client that does not accept HTML, or in forward-auth mode
403Policy or WAF denial, banned address, honeypot
413Solution body larger than the 64 KB connection buffer
417Unsupported request expectation; 100-continue is handled locally
429GCRA limit exceeded; Retry-After in seconds
431Request head over 16 KB
502Origin unreachable, or its response head was malformed or larger than 16 KB
503Connection limit (--max-connections) reached; the socket is closed after the reply

13.4 Error Catalog

INVALID COMMAND LINE stops startup for any option the daemon does not recognise, any value outside its documented range, and an invalid, missing or duplicate mode selection. The block names the option, the value given, the range expected and the error (for example UnknownOption, InvalidValue, InvalidMode, DuplicateMode, TooManyPeers). Supply --mode reverse_proxy or --mode forward_auth once; -m is the equivalent short option.

Implementation: core.explainError Maps every domain error to a boundary line, an explanation, and a Hint:. in libs/core/src/errors.zig
ErrorCauseHint
MalformedChallengeThe identifier is not a well-formed challenge recordFetch a fresh challenge and submit it unchanged
InvalidChallengeTagThe tag does not authenticate; not issued by this cluster or editedChallenges cannot be forged; request a new one
ChallengeExpiredOlder than the challenge TTL, or minted in the futureRequest a new challenge
FingerprintMismatchSubmitted from a different address or User-AgentSubmit from the client that fetched it
DifficultyNotMetHashcash nonce lacks the required zero bitsKeep searching nonces
InvalidProofThe sequential-work proof does not open the committed labelsRun the prover to completion for the issued depth and openings
WrongSolutionTypeA nonce for a PoSW challenge or a proof for HashcashMatch the solution field to the algorithm
DoubleSpendAttemptThe challenge was already spentChallenges are single use
StoreFullSpent set shard exhaustedLower the challenge TTL or raise capacity
InvalidTokenSignatureCookie tag or signature failsRe-authenticate through the interstitial
TokenExpiredCookie past its expiryRe-authenticate
TokenBoundAddressMismatchCookie presented from a different client identityCookies cannot be shared

13.5 Policy Schema

{
  "default_action": "ALLOW" | "DENY" | "CHALLENGE",
  "waf": true | false,
  "thresholds": { "challenge_at": int, "deny_at": int, "bits_step": int },
  "ip_rules": { "<cidr>": "ALLOW" | "DENY" | "CHALLENGE", ... },
  "rules": [
    {
      "name": "<kebab-case>",
      "path" | "path_regex": "<pattern>",
      "user_agent" | "user_agent_regex": "<pattern>",
      "headers" | "headers_regex": { "<Header>": "<pattern>" },   // up to 4
      "remote_addresses" | "cidrs": ["<cidr>", ...],             // up to 8, IPv4 or IPv6
      "action": "ALLOW" | "DENY" | "CHALLENGE" | "WEIGH",
      "weight": int,                                             // WEIGH only
      "challenge": { "difficulty": <work bits>, "algorithm": "hashcash" | "posw" }
    }
  ]
}

Pattern grammar: .* or * match anything; ^…$ anchors an exact path; a trailing *, /*, or .* is a prefix; a pattern starting with / is an exact path; anything else is a case-insensitive substring.

13.6 Storage Tables

policies, ip_reputation, security_incidents, incidents_fts (FTS5 over path and payload), incidents_vec (vec0, 64-float cosine embeddings), and sibuna_meta. Incident ids are node_id << 40 | sequence, unique across a cluster without coordination.

13.7 Build Targets

CommandResult
zig buildDaemon with storage, benchmark binary, browser module
zig build -Dstorage=falseFully static daemon without Zaxonlite
zig build -Dcluster=trueDaemon with Multi-Paxos replication (needs OpenSSL 3)
zig build testUnit tests, solver tests, end-to-end tests against a live daemon, storage tests
zig build fmtzig fmt --check plus the 70-line / 99-column style gate
zig build wasmThe browser module only
zig build benchmarkrun-all.sh: ReleaseFast benchmarks with host metadata
zig build book / zig build sidThis book / the SID records
Exercise 10.1. A client receives 400 with the title CLIENT FINGERPRINT MISMATCH after switching from Wi-Fi to cellular mid-solve. Explain the cause from the token and challenge formats, and propose the smallest change to the interstitial that recovers gracefully.
Explain the invariant. Without looking, list the endpoints a reverse proxy in front of Sibuna must route to the daemon rather than to the origin, and say why each is needed.
Search the documentation