a love letter to tangled (android, iOS, and a search API)
1---
2title: API Service Reference
3updated: 2026-03-26
4---
5
6Twisted is a Go service that indexes Tangled content, serves search, and caches
7recent activity. It uses PostgreSQL for the primary runtime and retains a
8temporary local SQLite fallback behind `--local`.
9
10## Runtime Modes
11
12| Command | Purpose |
13| --- | --- |
14| `api` | HTTP API server |
15| `indexer` | Tap consumer and index writer |
16| `migrate` | apply embedded SQL migrations |
17| `backfill` | register repos with Tap |
18| `enrich` | fill missing repo names, handles, and web URLs |
19| `reindex` | re-upsert documents and finalize the search index |
20| `healthcheck` | one-shot config and process probe |
21
22## HTTP API
23
24- `GET /healthz` — liveness probe
25- `GET /readyz` — readiness probe, checks database reachability
26- `GET /search` — keyword search
27- `GET /documents/{id}` — fetch one indexed document
28- `GET /admin/status` — cursor and queue state when admin routes are enabled
29
30The API also serves the built-in search/docs site from `/` and `/docs*`.
31
32## Search
33
34Keyword search is implemented with PostgreSQL full-text search.
35
36- weighted fields: title, author handle, repo name, summary, body, tags
37- query parser: `websearch_to_tsquery('simple', ...)`
38- ranking: `ts_rank_cd`
39- snippets: `ts_headline`
40
41Response shape stays the same as the previous FTS5 API. Ranking and snippet
42details are allowed to differ from the SQLite-era implementation.
43
44## Database
45
46Primary backend: PostgreSQL.
47
48Main tables:
49
50- `documents`
51- `sync_state`
52- `identity_handles`
53- `record_state`
54- `indexing_jobs`
55- `indexing_audit`
56- `jetstream_events`
57
58`documents` stores a generated weighted `tsvector` column plus a GIN index for
59keyword search.
60
61No embedding tables are active yet. `llama-embeddings` is deployed only as
62infra groundwork for a later semantic-search milestone.
63
64## Configuration
65
66Primary env vars:
67
68- `DATABASE_URL`
69- `HTTP_BIND_ADDR`
70- `INDEXER_HEALTH_ADDR`
71- `TAP_URL`
72- `TAP_AUTH_PASSWORD`
73- `INDEXED_COLLECTIONS`
74- `READ_THROUGH_MODE`
75- `READ_THROUGH_COLLECTIONS`
76- `READ_THROUGH_MAX_ATTEMPTS`
77- `ENABLE_ADMIN_ENDPOINTS`
78- `ADMIN_AUTH_TOKEN`
79
80Default local database URL:
81
82```sh
83postgresql://localhost/${USER}_dev?sslmode=disable
84```
85
86`--local` is deprecated and switches to the legacy SQLite fallback at
87`packages/api/twister-dev.db`.
88
89## Local Operation
90
91Start local Postgres with the repo compose file:
92
93```sh
94just db-up
95just api-build
96DATABASE_URL="postgresql://localhost/${USER}_dev?sslmode=disable" \
97 ./packages/api/twister migrate
98just api-dev
99just api-run-indexer
100```
101
102That dev compose file also runs Tap locally at `ws://localhost:2480/channel`.
103`api` and `indexer` no longer auto-apply schema changes on startup.
104
105Use `just api-dev sqlite` only when you need the temporary SQLite rollback path.
106
107## Deployment
108
109Production uses:
110
111- `docker-compose.prod.yaml` as the VPS stack source of truth
112- in-stack PostgreSQL on `pgvector/pgvector:pg17`
113- a one-shot `migrate` service before long-lived services start
114- private Tap and llama.cpp embedding services on the internal Compose network
115
116See `docs/reference/deployment-walkthrough.md` for the full production flow.