# kotobase — full reference (for LLMs/agents) kotobase (https://kotobase.net) is a Knowledge Graph BaaS and content-addressed graph hosting service built on kotoba (https://github.com/etzhayyim/kotoba). It serves engineers and enterprises that need managed graph databases, KG ingest, Datalog, SPARQL/Cypher-style query, MCP access, and durable provenance. Canonical graph truth is a signed immutable IPLD commit DAG: reads name a commit CID or frontier and writes return a new commit CID. A pin is an availability policy for immutable CID blocks; a storage provider never chooses graph truth. Identity: did:web:kotobase.net. Legacy alias host: kotobase.gftd.ai. ## Canonical Datomic Client API Base: https://datomic.kotobase.net . Operations are POST-only: /api/q /api/qseq /api/transact /api/pull /api/datoms /api/db /api/tx-range /api/with Bodies are Datomic Client API-shaped arg maps encoded as `application/edn` or `application/transit+json`. Authentication is the tenant Bearer JWT or CACAO described below. `https://kotobase.net/api/*` is a compatibility alias to the same handler; both origins advertise `datomic.kotobase.net` as canonical. This is shape compatibility, not proprietary Datomic wire compatibility. A stock Datomic client cannot connect to it. XRPC `datomic.*` below is legacy, reaches the same graph database, and is a 2026-11-01 removal candidate. ## Obsidian https://kotobase.net/obsidian describes the local-first workflow. Markdown, frontmatter, and wikilinks remain ordinary vault files while a tenant graph supports cross-note queries and bounded AI context. The plugin preserves concurrent remote edits as conflict files instead of silently overwriting local work. Attachments, Canvas files, and Obsidian plugin settings are outside the current sync scope; installation currently uses the repository build. ## Authentication Tenant calls accept either `Authorization: Bearer `, where the auth JWT `sub` is the tenant DID, or `Authorization: CACAO ` with `x-kotoba-did: `. The CACAO must grant the route's required capability over the tenant DID as graph scope; the official edge verifies the signature, issuer, audience, time, nonce, and tenant 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 (legacy for Datomic operations) POST https://kotobase.net/xrpc/ai.gftd.apps.kotobase. , JSON body, Bearer JWT or CACAO. Pinning & account: - pinCreate {name, cid?|quads?, size_hint_bytes?} -> {ok, pin_id, cid, status, size_bytes} - pinList {status?, limit?, offset?} -> {ok, pins:[{pin_id, name, cid, status, size_bytes, created_at}], total, offset, limit} - pinDelete {pin_id} -> {ok} - accountCreate {} ; accountStatus {} -> {ok, tenantDid, tier, quotaPins, quotaBytes, usedPins, usedBytes, storage_mode?} - usageGet {} -> {ok, tenantDid, pinCount, totalBytes, quotaPins, quotaBytes, storage_mode?} Worker-B2 keeps snake_case compatibility fields such as quota_pins, quota_bytes, used_pins, and used_bytes for older account/usage clients. 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 / view - graph.sparql {query, graph?, limit?} ; graph.query (SPARQL/Cypher over the Datom head) - kg.query {lang:"sparql"|"cypher"|"sql", query, limit?} - web.search {q, db_name?|graph?, limit?} -> {ok, count, results:[{url, title, snippet, score}]} (webpage keyword search: one capped datomic.q Datalog scan + edge-side ranking; ASCII + Japanese/CJK-bigram tokens) - geo.search {bbox|near:{lat,lon,radius_m}, q?, db_name?|graph?, limit?} -> {ok, count, results:[{id, name, lat, lon, distance_m?}]} (maps/place search: one capped datomic.q scan + edge-side bbox/haversine filtering and distance ranking) 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:[...]} - web.ingest {url, title?, text?, lang?, db_name?, cacao_b64} -> datomic.transact passthrough (caller-extracted page -> :webpage/* datoms; same tenant-cap CACAO gate as datomic.transact; the edge does not crawl) - geo.ingest {name, lat, lon, id?, tags?, db_name?, cacao_b64} -> datomic.transact passthrough (place -> :place/* datoms; id defaults to a deterministic slug of name) - datomic.transact / with are the tenant-capability Datom write plane when enabled; the edge derives the tenant graph from db_name and the caller DID so a raw client-supplied graph cannot override ownership. Generic /xrpc/:nsid allowlist (exact): Read/query: - `ai.gftd.apps.kotobase.datomic.q` - `ai.gftd.apps.kotobase.datomic.pull` - `ai.gftd.apps.kotobase.datomic.pullMany` - `ai.gftd.apps.kotobase.datomic.datoms` - `ai.gftd.apps.kotobase.datomic.seekDatoms` - `ai.gftd.apps.kotobase.datomic.indexRange` - `ai.gftd.apps.kotobase.datomic.indexPull` - `ai.gftd.apps.kotobase.datomic.entity` - `ai.gftd.apps.kotobase.datomic.ident` - `ai.gftd.apps.kotobase.datomic.entid` - `ai.gftd.apps.kotobase.datomic.asOf` - `ai.gftd.apps.kotobase.datomic.since` - `ai.gftd.apps.kotobase.datomic.history` - `ai.gftd.apps.kotobase.datomic.tx` - `ai.gftd.apps.kotobase.datomic.txRange` - `ai.gftd.apps.kotobase.datomic.log` - `ai.gftd.apps.kotobase.datomic.basisT` - `ai.gftd.apps.kotobase.datomic.dbStats` - `ai.gftd.apps.kotobase.datomic.sync` - `ai.gftd.apps.kotobase.datomic.view` - `ai.gftd.apps.kotobase.graph.sparql` - `ai.gftd.apps.kotobase.graph.query` - `ai.gftd.apps.kotobase.kg.query` - `ai.gftd.apps.kotobase.web.search` - `ai.gftd.apps.kotobase.geo.search` - `ai.gftd.apps.kotobase.mail.list` Tenant write: - `ai.gftd.apps.kotobase.kg.ingest` - `ai.gftd.apps.kotobase.kg.ingest_batch` - `ai.gftd.apps.kotobase.web.ingest` - `ai.gftd.apps.kotobase.geo.ingest` - `ai.gftd.apps.kotobase.mail.send` - `ai.gftd.apps.kotobase.mail.bind` - `ai.gftd.apps.kotobase.mail.ingestInbound` - `ai.gftd.apps.kotobase.datomic.fold` 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) POST https://kotobase.net/mcp — Model Context Protocol facade over streamable HTTP; tools are generated from the same ai.gftd.apps.kotobase.* lexicons (pin, account, query, ingest). Authentication is forwarded from the MCP request, so a tool can reach exactly what the caller could reach directly. `kotobase mcp` emits the client configuration. ## D. Canonical CID retrieval and contribution Authenticated clients fetch exact immutable bytes with `GET /ipld/` and may contribute exact immutable bytes with `PUT /ipld/`. Every response or upload is verified against the requested CID. `PUT` improves availability only; it cannot advance a graph head or change graph truth. Canonical clients read from a caller-supplied signed commit CID/frontier, query and cache locally, and may publish CID-bound query-result artifacts for other clients to verify and reuse. See the `/ipld/*`, `/v2/query`, `/v2/commits/*`, and `/v2/query-results/*` contracts in https://github.com/kotoba-lang/net-kotobase/blob/main/docs/API-CONTRACT.md . The compatibility pin/retrieval service remains available at https://ipfs.kotobase.net/ipfs/ and through standard IPFS gateways/nodes. It is an availability surface, not the canonical graph authority. ## E. Canonical durability and legacy archive compatibility R2, Backblaze B2, generic S3, IPFS providers, peer caches, and client caches are provider-neutral availability replicas behind the content-addressed BlockStore boundary. R2 is used only as an S3-compatible immutable mirror. Cloudflare Durable Objects, Cloudflare D1, PostgreSQL, and mutable provider heads are not dependencies of official read, write, merge, recovery, or query-cache correctness. A client accepts only CID-verified bytes and signed commits. Worker-B2 archive mode is a legacy compatibility mode for PSA `/pins` metadata and CAR archive reads. It is not an IPFS swarm node and is not the canonical CID-native data plane. Its historical Durable Object quota-locking write variant is excluded from the official deployment; it must remain fail-closed unless replaced by a provider-neutral mechanism. Worker-B2 keeps snake_case compatibility fields for older account and usage clients. See ADR-2606110003 for the historical mode and ADR-2608082300 for the superseding canonical decision. ## F. Quotas, plans & responsibility accountStatus returns tier + quotas, plus plan / plan_name / responsibility. There are three plans, separated by who carries operational responsibility. p2p (free, self-serve, and the plan every unprovisioned tenant is on): the operator carries NO responsibility — no availability commitment, no service credits, no incident-response obligation, no support obligation. 512 MiB / 50 pins on the shared endpoint; unmetered if you run your own peer. secure-managed (self-serve Stripe subscription): Gftd Japan K.K. operates, monitors, patches and recovers the service and is accountable for it. 99.9% monthly availability SLA with service credits (10% below 99.9%, 25% below 99.5%, 50% below 99.0%), one-business-day incident acknowledgement, RPO 0 / RTO under 10 minutes for a logical restore, 500 GiB / 10,000 pins included. enterprise (sales-led contract, hello@gftd.co.jp): proposed dedicated tenant and infrastructure, CACAO-only posture, read audit, customer-held key custody, residency, 24x7 named support and 99.999% availability are gated on a signed contract and qualified deployment. The DPA template still requires counsel approval and no approved BAA is offered today. Honest status: SOC 2 Type II, ISO/IEC 27001 and ISMAP are in preparation — no report or certificate has been issued. The 99.999% SLA is signed per contract on a dedicated deployment that clears the readiness gates in docs/PLANS.md; the shared production endpoint does not carry it, and there is deliberately no self-serve checkout for enterprise (POST /billing/checkout with tier=enterprise returns 400). Legacy tier strings still resolve so existing clients keep working: free -> p2p; starter / standard / pro -> secure-managed; regulated -> enterprise. Worker-B2 also keeps quota_pins / quota_bytes compatibility fields for older clients. ## 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.