Files
zeekay 214621b6ca Serve the hub at api.hanzo.ai/v1/skills
Every route mounts under /v1/skills, the address api.hanzo.ai routes to
this API; auth, personas, users, tokens, upload, integrations, search and
whoami are sub-resources and RESERVED keeps them from being slugs. The
web, the OG renderers, the CLI's default registry, /.well-known/bothub.json
and openapi.json name api.hanzo.ai. Sign-in calls back to
api.hanzo.ai/v1/skills/auth/callback and hands the token over in the
fragment.

The store starts again. docker-entrypoint.sh read Base's port from
BASE_PORT, which Kubernetes sets to tcp://<ip>:<port> in a namespace
holding a Service named base; Base now listens on 127.0.0.1:8090, fixed.
The collections migration is rewritten for the Base the Dockerfile pins
(1.5.91): flat field options, declared created/updated, and the users
collection every Base already has, extended rather than created twice.

Reads are anonymous against public rules, since Base takes IAM
credentials only and no longer serves password sign-in; writes answer
503 saying so. The catalogue migration, generated by scripts/catalog.mjs,
publishes the 83 skills in hanzoai/bot@8be8f1dc7b. /health reports the
store, so the probes restart a pod whose Base is gone.

cache.ts decides caching once: an anonymous 2xx GET is public for a
minute (five at the edge) with a weak ETag and 304s; a credential, a
write or an error is private, no-store. Filter values go through lit().
Embeddings go through api.hanzo.ai and are skipped without a key.

k8s/ was a second declaration of the deployment; universe is the one.
2026-09-30 01:10:48 -07:00

3.7 KiB

Repository Guidelines

Project Structure & Module Organization

  • src/ — TanStack Start app code (routes, components, styles); src/lib/api.ts is the one API client.
  • api/ — the API (Hono on :3001), talks to Hanzo Base via @hanzo/base.
  • base/hz_migrations/ — Base collections.
  • packages/bothub — the bothub CLI; packages/schema — shared schemas + ApiRoutes.
  • docs/ — product/spec docs (see docs/spec.md, docs/deploy.md).
  • public/ — static assets (/openapi.json, /.well-known/bothub.json).

Routes

  • The API is api.hanzo.ai/v1/skills and nothing else: api/src/app.ts mounts every route under /v1/skills (auth, personas, users, tokens, upload, integrations, search, whoami are sub-resources, so RESERVED in routes/skills.ts keeps them from being slugs). The web (src/lib/api.ts), the OG renderers, the CLI (DEFAULT_REGISTRY, /.well-known/bothub.json) and public/openapi.json all call it there; src/__tests__/v1-prefix.test.ts fails on a first-party path outside /v1/skills, a site host used as an API base, or anything under /api.
  • Ingress: universe routes Host(api.hanzo.ai) && PathPrefix(/v1/skills) to :3001. hub, market and skills.hanzo.bot serve only the web; their /v1/… answers 308 to the api.hanzo.ai equivalent.
  • Caching is decided once, in api/src/lib/cache.ts: an anonymous 2xx GET is public, max-age=60, s-maxage=300, stale-while-revalidate=600 with a weak ETag (304 on a match); anything with Authorization, any write, any error is private, no-store. CORS is * with no credentials: callers send a bearer, never a cookie.
  • Sign-in is Hanzo IAM (hanzo-bothub): /v1/skills/auth/login?return=<site> → hanzo.id/v1/iam/oauth/authorize with redirect https://api.hanzo.ai/v1/skills/auth/callback (universe iam-provision.yaml); the callback lands on the site with #session=<token>.
  • Store: Base runs in the pod on 127.0.0.1:8090, pinned in the Dockerfile. It takes credentials from IAM only and serves no password sign-in, so reads are anonymous (public rules) and every write answers 503 until the API holds an IAM identity. Its data is the container's: the catalogue (base/hz_migrations/1709000001_catalog.js, generated by scripts/catalog.mjs from hanzoai/bot's skills/) is applied on every start. /health is 503 while Base is down, so the probes restart it.
  • Release: build at the door (POST https://api.hanzo.ai/v1/build), pin tag + digest in universe charts/app/values/hanzo/bot-hub.yaml.

Build, Test, and Development Commands

  • bun run dev — local app server at http://localhost:3000.
  • bun run build — production build (Vite + Nitro).
  • bun run preview — preview built app.
  • bun run lint — oxlint (type-aware).
  • bun run test — Vitest (unit tests).
  • bun run coverage — coverage run; keep global >= 70%.

Coding Style & Naming Conventions

  • TypeScript strict; ESM.
  • Indentation: 2 spaces, single quotes.
  • Lint/format: oxfmt + oxlint (type-aware).

Testing Guidelines

  • Framework: Vitest 4 + jsdom.
  • Tests live in src/** and packages/**/src.
  • Coverage threshold: 70% global (lines/functions/branches/statements).

Commit & Pull Request Guidelines

  • Commit messages: Conventional Commits (feat:, fix:, chore:, docs:…).
  • Keep changes scoped; avoid repo-wide search/replace.
  • PRs: include summary + test commands run. Add screenshots for UI changes.

Git Notes

  • If git branch -d/-D <branch> is policy-blocked, delete the local ref directly: git update-ref -d refs/heads/<branch>.

Configuration & Security

  • Local env: .env.local (never commit secrets).
  • VITE_API_URL moves the API origin for local work (http://localhost:3001); unset, it is api.hanzo.ai.