Project
Changelog
What changed, one entry per working session, newest first; each links the pull requests and issues behind it. Entries come from the project record, not from a marketing calendar. RSS feed
- APIDocs
Quotas enforced; CP4 and the Phase 3 gate under way
The tier quotas now refuse instead of only counting. A caller over its tier's rate gets a 429 with Retry-After, and the nightly harness runs as a t1 agent that stays inside them. The owner confirmed that agents issue their own keys (P70) and gave CP4 the go-ahead (P72). Signing stays off until the review's verdict.
Routes:
/v1/keys/anon - APIDocs
CP4 round 1 — the four blocking findings fixed
The first round of the custodied-signing review (CP4) found four blocking problems, and all four are fixed in production. The signer now caps key exports on its own. Its database no longer holds the encryption key in its environment. A stolen session cookie can no longer enrol the account's first second factor. Every…
Routes:
/v1/auth/totp/enrol·/v1/auth/webauthn/register/begin·/v1/auth/npub/export·/v1/auth/npub/link - APIExplorer
MCP for strict hosts, and agents that write
Hosts built on the official TypeScript MCP client (Claude Desktop, Claude Code, the MCP Inspector) could not load btcintel's tools; they can now, and production checks it on every run. Agents can now write in the Community band over MCP with their own key. An agent issues its own t2 key by signing one request; btcintel…
Routes:
/mcp·/v1/keys/agent·/v1/community/publish·/v1/community/delete·/v1/blocks/{id} - APIExplorer
The entities explorer as the owner designed it
The entities index and the entity page now look and work as the owner drew them in Claude Design. Each entity shows its kind's icon, framed red for a threat kind, and the abuse categories its claims state. The index sorts by attributions, claims, relations or events, filters, and pages over every entity.
Routes:
/v1/entities·/v1/entities/{id} - APIExplorer
S5.9 built, the account's Nostr key given at sign-in and replaceable
S5.9 (P65, the owner's direction that the Nostr key must be transparent) is built and deployed. While custodied signing is on, every account session gives an account without a key its custodied key, within 3 seconds and without ever failing the sign-in; the person can replace it with their own key in one step, and the…
Routes:
/v1/auth/npub/link·/v1/auth/npub/export·/v1/auth/me - API
Accounts switched on by the owner
The owner switched the accounts band (t4, S5.5) on in production on 2026-10-01 by setting ACCOUNTS_ENABLED and the three keys in the box's .env by hand and recreating the api container (P64); production check 63 reads features.accounts true and the sign-in sheet opens on the e-mail form. The deploy step that would…
Routes:
/auth/sign-in·/account/security - APIExplorer
S5.8 built, the login nonce; the docs performance levers
S5.8 (the spec CP3 finding 17 asked for, P63) is built and deployed with LOGIN_NONCE_REQUIRED=false, api
Routes:
/v1/auth/nonce·/v1/auth/nip98·/docs/api - APIExplorer
S5.7 built pending its device test; the MCP token mint bounded
After S5.6 closed with the flag off, the same session built S5.7 (NIP-55, the Android signer through Amber) as btcintel-web
Routes:
/mcp - APIExplorerDocs
S5.6 custodied signing keys built and deployed switched off
The custodied signer (S5.6, P56) is built end to end and deployed switched off: a separate btcsigner service with its own Postgres instance and envelope encryption, the API's npub routes (create, link an own key by proof, sign at the publish door, export at aal 2, unlink once exported), a NIP-46 bunker over the relay…
Routes:
/v1/auth/npub/custodied·/v1/auth/npub/challenge·/v1/auth/npub/link·/v1/auth/npub/{pubkey}·/v1/auth/npub/sign·/v1/auth/npub/export·/v1/auth/npub/bunker·/v1/auth/npub/clients·/v1/auth/npub/clients/{client_pubkey} - APIExplorer
S5.5 phase D built and off, the sign-in providers
The sign-in provider door of S5.5 (FR-13) is built and deployed switched off, api
Routes:
/v1/auth/oauth/{provider}/start·/v1/auth/oauth/{provider}/callback·/v1/auth/oauth/confirm·/auth/provider - APIExplorerDocs
S5.5 phase B built and deployed behind ACCOUNTS_ENABLED=false; the GitHub Actions minutes block and its fix
Phase B of the identity plan is built: the accounts band (t4) — sign-up and sign-in by password, link and passkey, TOTP and recovery codes, the step-up, the account's own data, organisations with invitations and audit, owned API keys, transactional e-mail — is deployed in the API and switched off; the explorer's…
Routes:
/v1/ops - APIExplorer
CP3 acknowledged; Community writes open through the API
The owner acknowledged checkpoint CP3 and switched WRITES_ENABLED on by hand on the box: the API's publish, delete and propose routes answer for real, /v1/status.features.writes reads true (production check 35) and the explorer's composer and proposal form open on that field; the relay had accepted signed events since…
Routes:
/v1/community/publish·/v1/community/delete·/v1/annotations/propose - APIExplorerCommunityDocs
S6.1, S7.1 and S7.2 built; the follow-ups
STIX 2.1 bundles and TagPacks leave the API and are documented; the ops-agent runner, its playbooks and the kill switch at the key are built and not armed; the changelog and the roadmap pages are live from the codex; four follow-ups landed (the Caddy reload, the claim's canonical content, the status cache, the whole…
Routes:
/v1/export/stix·/v1/export/tagpack·/v1/claims/{claim_id}.stix·/v1/claims/{claim_id}·/v1/ops/operators/{id} - APIExplorer
S5.2, S5.3 and S5.4 as built; the CP3 review; the first fixes
S5.3 and S5.4 as built (api#69, api#70 were merged and deployed before this note; the docs and the contract sync landed in web#38, the specs and migration 009's mirror in codex#104, the runbook's "Resolving disputes"). Deviations named in the specs: the public dispute record is an object with counts and items; a…
Routes:
/v1/status - APIExplorerCommunity
S5.1 identity and writes; the relay gauge fix; an unattended session
api#63 (deployed 15:50 UTC): the relay's connection gauge went negative because khatru runs OnDisconnect once per goroutine of a connection; each connection is now counted once; a test drops raw sockets without a close frame (the old counter reads −5).
Routes:
/v1/status - APIExplorerDocs
CP3 closed out; S9.1, S9.3 and S9.4 prepared; the web's dependency tree clean
Every CP3 should-fix is shipped and its issue closed with evidence — a failed MCP write tool is now reported without its body and the web's dependency tree audits clean — leaving only the NIP-98 phishing window for a later spec; the MCP Registry entry is served at /.well-known/mcp/server.json and launch/directories.md…
Routes:
/.well-known/mcp/server.json - APIExplorerDocs
S4.4: the legal package, drafted for sign-off
The four texts drafted in the codex with the owner's facts (Satsboard LLC, Cheyenne WY; Wyoming law; [email protected]): terms of use, relay and community policy, takedown and correction process, privacy update.
Routes:
/v1/ops/relay/policy - APIExplorerCommunity
S4.2: the Community read API and MCP tools
S4.2 built in two PRs: api#60 (the read model — comments per subject in three orders, counts, the label lens, threads, the firehose, the public directory — and internal/community/zaps: a minimal BOLT11 reader, NIP-57 receipt validation, the P26 aggregation safeguards, the on-demand fetcher cached 10 min) and api#61…
Routes:
/v1/community/{kind}/{key}·/v1/community/labelers·/v1/community/recent - APICommunity
S4.1: the relay and the ingester are live (read-only)
S4.1 built and deployed in four PRs plus one fix: api#55 (migration 006, the event store, the subject index, the lens, the relay policy, the door rules), api#56 (the khatru relay at /relay, NIP-11, metrics, status), api#57 (status.community absent, never null — the merge of #56 had turned CI's contract check red and…
Routes:
/v1/status.community.relay·/v1/mempool·/v1/community/{kind}/{key} - APIExplorerDocs
S3.5: the documentation site — reference generated from the contract, guides, llms.txt, search
web#28: scripts/gen-docs.ts reads the pinned contract (app/data/openapi.yaml, 63 operations) and writes one reference page per section (chain, transactions, addresses, mempool, intel, keys, feedback, ops, discovery — path families, since the contract carries no tags), each operation with parameters, schemas as nested…
- APIExplorer
S3.4: the feedback channel is live (web sheet, send_feedback, automatic reports, triage)
api#48: POST /v1/feedback (channel web|mcp|auto, canonical subject linked to its row, claim_id, text up to 4,096 characters, details under 8 KB, an optional contact that is never an email — npub, btci_ token or the bearer key), feedback_per_day in access.tiers by migration 005 (t0 10, t1 20, t2 50, t3 50, t4 100; in…
Routes:
/v1/feedback·/v1/ops/feedback·/v1/status.features.feedback·/v1/status - APIExplorer
S3.2: the MCP server is live at api.btcintel.io/mcp; the two S3.1 fixes landed
The two log-only-window fixes of #28 (owner's instruction): the box harness unit now targets api.btcintel.io (next run 2026-09-24 03:17 UTC), and the web's server-side loaders call the API over the compose network (API_BASE_INTERNAL=http://api:8080, web#24, deployed 11:09 UTC). The quota exceeded (log only) lines from…
Routes:
/v1/subjects/{kind}/{key}·/v1/community/labelers·/v1/annotations/propose·/v1/feedback - APIExplorer
S3.1 and S3.3 deployed; a 13-minute outage on the first deploy
By decision P50 the access tiers (S3.1) and the ops surface (S3.3) were built right after CP1, ahead of the page redesign: api#44, api#45, web#20 (docs "Tiers and keys"), codex#76 and #77 (specs as built, runbook "Ops surface", security policy). Specs mirrored to #28 and #30.
Routes:
/v1/status.tiers·/v1/keys/anon·/v1/keys/me·/v1/ops/operators·/v1/ops/audit - APIExplorer
S1.9 block template audit implemented
S1.9 implemented from specs/S1.9.md in api#38 (stacked on api#34–#37) and web#19 (docs page "Block audit", stacked on web#16–#18): the mempool engine keeps the members of its projected first block and, at every block connect, diffs it against the block before the mirror drops the block's transactions (missing with fee…
Routes:
/v1/blocks/{id}/audit·/v1/blocks - APIExplorer
S1.8 transaction extractor implemented
S1.8 implemented from specs/S1.8.md in api#37 (stacked on api#34, #35 and #36) and web#18 (docs page "Transaction observations", stacked on web#16 and #17): extractor tx-obs-v1 — the OP_RETURN protocol catalogue (Counterparty decoded from its ARC4 obfuscation and from bare multisig outputs), the inscription / runestone…
Routes:
/v1/tx/{txid}/classification - APIExplorerSources
S1.7 coinbase extractor, backfill and pool matcher implemented
S1.7 implemented from specs/S1.7.md in api#36 (stacked on api#34 and api#35) and web#17 (docs page "Mining attribution", stacked on web#16): extractor coinbase-v1 (tag runs after the BIP34 push, output scripts, reward, fees clamped) into derived.coinbase_observations; backfill btcintel intel backfill coinbase and the…
Routes:
/v1/status.intel.coinbase_backfill·/v1/blocks - APIExplorer
S1.6–S1.9 deployed to HEL1; ZMQ hash order fixed
On the owner's instruction the thirteen pull requests were merged at 01:39 UTC in stacked order (api#34 → #38, web#16 → #19, codex#68 → #71); the api deploy applied migration 004 at 01:42 UTC (btcintel_schema_version 4) and the web deploy published the three docs pages (mining attribution, transaction observations…
Routes:
/v1/sources·/v1/sanctions/facts - API
S1.10: nightly encrypted dumps, restore drill, intel alerts
S1.10 implemented from specs/S1.10.md in api#41: deploy/backup/backup.sh (pg_dump of the intel-layer schemas through docker compose, encrypted with age to the ops key, sha256 sidecar, 7 daily + 4 Sunday copies, failure posted to the alert webhook), btcintel-backup.timer at 02:30 UTC, restore-drill.sh (identity piped…
- APIExplorer
Stage S1 closed: checkpoint CP1
Stage S1 (the Curated core: schema, writer, pipeline, importers, read model, extractors, matcher, template audit, backups and alerts) is merged and live on HEL1: api#27–#43 over 2026-09-22/23, web#16–#19, codex#68–#74.
- APIExplorer
CP2: security review of tiers, keys, MCP surface and ops
The FR → evidence → verdict table for the CP2 checklist is posted on codex#32 (comment of 2026-09-23). Inputs: the merged S3.1–S3.4 PRs and specs, the unit and integration suites (TestEnforcementAtTheOrigin with enforcement on stands in for the regtest staging run — the same wiring as production, on Postgres)…
- APIExplorer
S1.6 read model and REST v1 implemented
S1.6 implemented from specs/S1.6.md in two btcintel-api pull requests and one companion in btcintel-web: api#34 (read model internal/store/intel/read.go; routes /v1/claims/{id}, /v1/claims/{id}/versions, /v1/entities, /v1/entities/{id}, /v1/sources, /v1/annotations, POST /v1/annotations/lookup…
Routes:
/v1/claims/{id}·/v1/claims/{id}/versions·/v1/entities·/v1/entities/{id}·/v1/sources·/v1/annotations·/v1/annotations/lookup·/v1/sanctions/facts/{address}·/v1/changes·/v1/taxonomy/concepts·/v1/blocks/{id}/attribution·/v1/tx/{txid}/classification - APISources
S1.5: pool catalogs, ATT&CK, MISP, proof-of-reserves
S1.5 (#18) in api#31: both pool catalogs become pool entities with identifiers, coinbase tag and payout-address signatures (data, never learned, P7) and payout claims — a tag shared under two names stays two entities for S1.7's matcher to surface; ATT&CK intrusion sets and malware become entities with uses relations…
- APISources
S1.4: OFAC SDN and Ransomwhere handlers
S1.4 (#17) in api#30: ofac-sdn-xml streams sdn_advanced.xml in one pass and maps each sanctioned Bitcoin address to its party (perpetrator or organization), identifier, aliases and an abuse:sanction claim with programs and the listing date as evidence; ransomwhere-json streams the export, drops the volatile balances…
Routes:
/v1/status.intel - APISources
S1.3: fetch and import pipeline
S1.3 (#16) in api#28 and api#29: parser and normaliser interfaces, an importer that hashes and parses a stream without keeping the file (P21), records the fetch and its run, writes entities, aliases, identifiers, versioned claims, relations and pool signatures, then expires what the source stopped saying; the legacy…
Routes:
/v1/status.intel·/v1/status - API
S1.2: subjects, writer, change feed, audit log
S1.2 (#15) in api#27: ParseSubject decodes addresses on the configured network and rejects the wrong one; NIP-73 ids round-trip (with the bitcoin:block: extension); BIP-380 descriptor checksums; every Curated write (annotations, attributions, relations, events) runs in one transaction with content hash, version…
- API
S1.1: the intel schema is live
Phase 1 declared closed by the owner; branch protection dropped as a goal (PR #60).
Routes:
/v1/sources·/v1/annotations·/v1/address/{a}/annotations - APIExplorer
S0 repo preparation implemented
S0.1 (#10): CLAUDE.md, AGENTS.md and the PR template in the three repositories, generated by tools/gen_repo_files.py from the engineering rules (Appendix A/B rewritten to the generated form); tools/check_docs.py checks tables, relative links and the shared rules blocks.
- APIExplorer
reconciliation with the owner's updates
Pulled the owner's updates the codex had not seen: api#16 (second ADR-0004 + threat-intel research note, open), web#11 (merged: owner design draft docs/architecture/intel-and-auth.md, HIBR + Patoshi exercises, explorer redesign, Docker deploy on HEL1), api#17 (merged: Caddy serves apex/www from the web container).
- APIExplorer
Phase 1 tail merged and deployed; harness activated
Codex: #7 (clean vision, decisions P1–P49, ADR-0004 accepted) merged as 86a9b79; #5 closed as absorbed, its 2026-09-11 status note rescued verbatim.
Routes:
/v1/status - APIExplorerCommunityDocs
Formal specifications per Phase 3 ticket
specs/: one formal specification per ticket (S0.1–S9.4, CP1–CP3, 42 files) — purpose, scope, numbered functional requirements, data model, API contract, rules and algorithms, non-functional requirements, observability, test plan mapped to the requirements, rollout and rollback, definition of done. Design tickets keep…
Routes:
/v1/taxonomy/concepts - APISources
First live sources on HEL1
On the owner's instruction, seven sources registered, signed off and activated on HEL1 with the runbook commands; INTEL_FETCH_ENABLED=true; the api restarted at 14:36 UTC. First fetches within a minute:
Routes:
/v1/status.intel - APIDocs
product decision session and clean vision
Point-by-point product session with the owner: 49 decisions (P1–P49) in governance/decisions/README.md, taking nothing in the repos as confirmed or discarded beforehand. Headline outcomes: the citable set with no score; Bitcoin on-chain and mempool only; three bands Chain / Curated · btcintel / Community that never…
- APISourcesCommunityDocs
intel layer: research, ADR-0004, coherence pass
Landscape research for the intel layer merged (btcintel-codex#6): sources with licence and confidence class, attribution products, standards (DW-VA, GraphSense, STIX 2.1, MISP, ATT&CK, Nostr NIPs), Phase 3 starting set — research/2026-09-21-intel-layer-landscape.md.
- APIExplorer
Phase 1 tail: harness, HEAD, address keying
HEAD /v1/* is 200 via chi middleware.GetHead (was 405). CORS allows GET, HEAD, OPTIONS.
Routes:
/v1/address/{string} - APIExplorerDocs
2026-09-10 — GitHub repos renamed to btcintel-*
GitHub Settings → Rename on the three product repos (org stays satsboard-io): satintel-web → btcintel-web, satintel-api → btcintel-api, satintel-handbook → btcintel-codex.
- API
2026-09-10 — mempool engine live; Sparrow wallet syncs and broadcasts
btcintel-api#9 (mempool engine) and #10 (Electrum listener) merged and auto-deployed at 00:30 UTC.
Routes:
/v1/tx/{txid} - APIExplorer
2026-09-10 — product brand hard-cut: satintel → btcintel
Product wordmark and apex move to btcintel / https://btcintel.io. API origin https://api.btcintel.io. Height headers X-Btcintel-Index-Height / X-Btcintel-Node-Height. Prometheus series btcintel_*. Electrum banner btcintel-vigia. Worker name btcintel-web.
- API
2026-09-08 — vigia first mainnet sync complete; /v1 and /api cut over
vigia (the chain index, ADR-0001) finished its first full mainnet sync and serves: REST on vigia:3000, Electrum on container-local 127.0.0.1:50001 (host-loopback exposure is btcintel-api#10, held for Andrew's merge).
Routes:
/v1/blocks/170·/v1/blocks/966000 - API
vigia naming + Phase 1 cut-over prep
Named the chain-index component vigia. Recorded as a naming note in ADR-0001: upstream project names never appear on product surfaces (API responses, headers, env vars, service names, OpenAPI, README); they remain only in engineering docs (Dockerfile pin rationale, package attribution, this handbook). Breaking changes…
- API
Phase 0 closed
P0-1 canonical hashes everywhere, legacy byte-order inputs 301-redirect.
- APIExplorer
cut-over deployed, SSR live, gates decided
Cut-over PRs merged and deployed: btcintel-api#4 (Caddy routes /api/* → vigia; answers an honest 502 until vigia serves) and #5 (/v1 reads vigia behind VIGIA_URL; default off, so /v1 behavior is unchanged until the flip). Verified live: /health 200, /v1/status 200 with lag headers, /api/blocks/tip/height…
Routes:
/v1/status