AgentsView
AgentsView indexes coding-agent session transcripts (Claude Code, Codex, Copilot CLI, and others) and reports token usage, cost, and searchable history.
It is local-first: every workstation runs its own copy against its own SQLite archive. This deployment provides a shared aggregation point. Each workstation pushes its sessions into a central PostgreSQL database, and the cluster serves one read-only dashboard over the union of them at https://agentsview.${SECRET_DOMAIN}.
Architecture
workstation A β agentsview pg push ββ
workstation B β agentsview pg push ββΌββΊ datasci16 (PostgreSQL) βββ agentsview pg serve (this app)
workstation C β agentsview pg push ββ β² β
datasci-db.${SECRET_DOMAIN} agentsview.${SECRET_DOMAIN}
(LAN, 10.2.118.7) (envoy-internal)
- Sync direction is one-way. Workstations push; nothing flows back. The dashboard is read-only β starring and pinning happen locally and travel with the next push.
- The pod holds no state.
pg servereads everything from PostgreSQL and runsEnsureSchemaon startup, so it comes up cleanly against an empty database and needs no PVC or volsync. pg pushspeaks the PostgreSQL wire protocol, not HTTP. Workstations therefore connect to the database directly, not through the gateway.
Configuration
| Setting | Value |
|---|---|
| Image | ghcr.io/kenn-io/agentsview |
| Mode | PG_SERVE=1 β entrypoint runs agentsview pg serve |
| Database | agentsview on the datasci16 cluster |
| Connection string | POSTGRES_URL from the database-agentsview secret, templated in db/postgres.yaml to carry sslmode=require |
Data dir (AGENTSVIEW_DATA_DIR) |
/data, an emptyDir β holds only config.toml |
| Ingress | envoy-internal, Authelia OIDC via SecurityPolicy |
--public-url=https://agentsview.${SECRET_DOMAIN} is not cosmetic. The server validates the Host header on every /api/ request against the addresses it bound to, as DNS-rebinding protection. Requests arriving through Envoy carry the public hostname, so without this flag the UI loads but every API call returns 403.
Client setup
Run this once per workstation.
Read the role and password the operator generated. It is one shared role, so every workstation uses the same credentials:
kubectl get secret database-agentsview -n datasci -o jsonpath='{.data.POSTGRES_URL}' | base64 -dThat URL is not usable as-is. Its host is
datasci16-rw, the in-cluster service name, which resolves only inside thedatascinamespace β off-cluster the lookup fails. Take the role and password from it and discard the rest.Add a
[pg]section to~/.agentsview/config.toml, using the LAN hostname and port:[pg] url = "postgresql://<role>:<password>@datasci-db.${SECRET_DOMAIN}:5432/agentsview?sslmode=require"Append it at the end of the file. A TOML table header captures every key below it, so
[pg]placed above existing top-level keys such asauth_tokensilently reparents them.Keep
sslmode=require: the client refuses a DSN that permits plaintext to a non-loopback host.machine_nameis optional and defaults to the OS hostname; set it only when that hostname is not what you want labelling this machineβs sessions.AGENTSVIEW_PG_URLworks too if you would rather keep the URL out of the file.Seed the database, then install the watcher so it stays current:
agentsview pg push agentsview pg service installagentsview pg push --watchruns the same loop in the foreground.
Semantic search (optional, off by default)
Text search works out of the box. Semantic search is wired but not switched on, and needs three things.
- The
vectorextension in theagentsviewdatabase.pgvectorships in the CNPG image β thesystemtag builds from thestandardstage, which installspostgresql-${PG_MAJOR}-pgvector. Butvectoris not a trusted extension, so theagentsviewowner role cannot create it. ThePostgresCR in db/postgres.yaml asks the operator to create it, which works only if the operator connects as a role holding superuser. If it doesnβt, the CR reports the failure and a superuser does it once by hand:
kubectl exec -n datasci "$(kubectl get pods -n datasci -l cnpg.io/instanceRole=primary -o name)" -- psql -d agentsview -c 'CREATE EXTENSION IF NOT EXISTS vector'Either way nothing crashes without it: AgentsView attempts the same statement during schema setup and logs a one-line notice when it is refused.
[vector]filled in and enabled in app/config/config.toml. An init container copies that file to/data/config.tomlon every pod start β a copy rather than a mount, because the server writes a generated auth token back into it and treats a write failure as fatal. Model identity must match the workstations exactly; those fields form the generation fingerprint, and a mismatch leaves the hub with no vectors it can compare against. Only the endpoint may differ. Withenabled = truethe pod refuses to start unlessmodel,dimension, and a server are all set, so a half-filled block fails loudly instead of silently serving nothing.An OpenAI-compatible embeddings endpoint the pod can reach, for encoding search queries. Nothing in the cluster serves one today.
Recovering from a schema incompatibility
pg serve migrates the schema forward on startup, but refuses to run against one it considers incompatible β most likely after an image bump. It exits with pg serve: schema incompatible and names the fix. There is no reset subcommand; the schema is dropped by hand.
Nothing here is recoverable from PostgreSQL alone, but nothing is lost either: every workstation still holds the authoritative SQLite archive, and a full push rebuilds the hub from them.
Suspend the app so it stops crash-looping into a half-dropped schema:
flux suspend hr agentsview -n datasciDrop the schema. It is named
agentsview(the[pg] schemadefault) and holds only pushed data:kubectl exec -n datasci "$(kubectl get pods -n datasci -l cnpg.io/instanceRole=primary -o name)" -- psql -d agentsview -c 'DROP SCHEMA IF EXISTS agentsview CASCADE'Resume.
EnsureSchemarecreates the tables at the current version on startup:flux resume hr agentsview -n datasciRepopulate from every workstation.
--fullis required β an incremental push would skip sessions it believes are already there:agentsview pg push --full
Until each machine has run step 4, the dashboard shows only the machines that have.
Dependencies
cnpg-cluster-datasciβ thedatasci16PostgreSQL clusterext-postgres-operator-datasciβ provisions the database and role from thePostgres/PostgresUserCRs indb/envoy-gateway(envoy-internal) andautheliafor ingress and loginexternal-secretsβ pulls the OIDC client secret fromdatasci.agentsview
Gotchas
- Every workstation shares one database role. The operator provisions a single owner per database, so pushes are not attributable by credential β only by the
machinename each client sets. - Deletes do not propagate. Removing a session locally leaves it in PostgreSQL; upstream expects manual SQL cleanup.
- Text search needs nothing.
EnsureSchemacreatespg_trgm, which is a trusted extension the database owner installs on its own. - Bumping the image can require a schema migration.
pg servemigrates forward on startup but refuses to run against a schema it considers incompatible; upstreamβs answer is to drop and recreate the schema, then re-push from each machine.