# kotobase — full reference (for LLMs/agents) kotobase (https://kotobase.net) is a content-addressed pin & hosting service built on kotoba (https://github.com/etzhayyim/kotoba), a Datomic-over-IPFS distributed knowledge graph. A pin = a named commit CID in kotoba's quad store; once pinned a CID is durable (off-site via CAR-on-B2) and addressable over IPFS. Identity: did:web:kotobase.net. Legacy alias host: kotobase.gftd.ai. ## Authentication Tenant calls accept either `Authorization: Bearer `, where the gftd auth JWT `sub` is the tenant DID, or `Authorization: CACAO ` with `x-kotoba-did: `. The CACAO must grant `kotobase:pin` over the tenant DID as graph scope; the pod verifies the signature and issuer binding. ## A. IPFS Pinning Service API (standard) Spec: https://ipfs.github.io/pinning-services-api-spec/ . Base: https://kotobase.net (clients append /pins). Native tooling: ipfs pin remote service add kotobase https://kotobase.net ipfs pin remote add --service=kotobase --name= ipfs pin remote ls --service=kotobase ipfs pin remote rm --service=kotobase --cid= Endpoints: - POST /pins body {cid, name?} -> 202 PinStatus - GET /pins?cid&name&status&limit -> {count, results:[PinStatus]} - GET /pins/{requestid} -> PinStatus | 404 - POST /pins/{requestid} body {cid, name?} -> replace (delete+create) - DELETE /pins/{requestid} -> 202 PinStatus = {requestid, status:queued|pinning|pinned|failed, created, pin:{cid,name,origins,meta}, delegates, info}. Errors = {error:{reason, details?}} with appropriate HTTP status. `requestid` is stable across create/list/delete (so rm-by-cid = list->delete works). ## B. XRPC API POST https://kotobase.net/xrpc/ai.gftd.apps.kotobase. , JSON body, Bearer JWT or CACAO. Pinning & account: - pinCreate {name, cid?|quads?, sizeHintBytes?} -> {ok, pin_id, cid, status, size_bytes} - pinList {} -> {ok, pins:[{pin_id, name, cid, status, size_bytes, created_at}], total} - pinDelete {pin_id} -> {ok} - accountCreate {} ; accountStatus {} -> {ok, tier, quota_pins, quota_bytes, used_pins, used_bytes} - usageGet {} Query (read; tenant JWT): - datomic.q {graph, query_edn, inputs_edn?, as_of?, since?, history?} -> {graph, rows_edn, ...} - datomic.pull / pullMany / datoms / entity / asOf / since / history / tx / txRange / log / basisT / dbStats / seekDatoms / indexRange / indexPull / sync - graph.sparql {query, graph?, limit?} ; graph.query (SPARQL/Cypher over the Datom head) - kg.query {lang:"sparql"|"cypher"|"sql", query, limit?} Write (tenant; JWT sub == tenant_did or CACAO issuer == tenant_did): - kg.ingest {id, type?, label_ja?, label_en?, claims?, relations?, ...} - kg.ingest_batch {entities:[...]} - (datomic.transact is operator-only and NOT exposed to tenants) Example: curl -s -X POST https://kotobase.net/xrpc/ai.gftd.apps.kotobase.pinCreate \ -H "authorization: Bearer $JWT" -H 'content-type: application/json' \ -d '{"name":"my-doc","cid":"bafyrei..."}' ## C. MCP (AI agents) https://mcp.gftd.ai/mcp — Model Context Protocol facade; tools are generated from the same ai.gftd.apps.kotobase.* lexicons (pin, account, query, ingest). ## D. Retrieval Pinned content is content-addressed; resolve by CID at https://ipfs.gftd.ai/ipfs/ or via any IPFS gateway/node. ## E. Durability (CAR-on-B2) Local durable tier: kubo (IPFS) blockstore on the pod PVC (KOTOBA_IPNS=kubo). Off-site: each commit's blocks are packed into ONE CAR object archived to Backblaze B2 (bucket ai-gftd-nats, key = commit CID). Object count is proportional to commits, not blocks, so it scales where a per-block S3 datastore does not. Cold reads are served from B2 via a single ranged GET against the CAR, located by a local index. See ADR-2606042100 (CAR-on-B2) and ADR-2606042200 (this PSA). Worker-B2 archive mode: operators can set KOTOBASE_STORAGE_MODE=b2 to serve the pin control plane from Cloudflare Worker + Backblaze B2 metadata without the Kubernetes pod. With KOTOBASE_IPFS_GATEWAY_URL it can archive gateway-produced IPFS/IPNS IPLD CARs and serve them from /ipfs/* and /ipns/*. This is an archive mode, not an IPFS swarm node; production currently remains in backend/Kubo mode and Worker-B2 mode verifies did:key EdDSA CACAO itself. Quota-safe metadata writes use a Durable Object tenant lock. EIP-191 / smart-account CACAO remains backend-only. See ADR-2606110003. ## F. Quotas & tiers accountStatus returns tier + quotas. Free tier ~ a few pins / ~100 MB; higher tiers raise quota_pins / quota_bytes. ## G. Roadmap - sk_live_* API keys for non-interactive clients (today: gftd-AUTHN JWT or CACAO). - PinStatus.delegates (swarm multiaddrs) to push blocks for a not-yet-public CID over bitswap.