# AbstractGateway

> Durable Run Gateway host for AbstractRuntime: start runs + append durable commands; clients replay/stream the durable ledger (replay-first).

This repository contains the `abstractgateway` Python package (CLI + FastAPI server + durable stores + runner worker).
The main HTTP surface is `/api/gateway/*` (FastAPI routes in `src/abstractgateway/routes/gateway.py`).
The live HTTP contract is the runtime OpenAPI schema: `GET /openapi.json` (Swagger UI: `GET /docs`).

Format note: follows the [llms.txt specification](https://llmstxt.org/).

Key invariants / defaults (evidence in code):
- Security is enabled by default for `/api/gateway/*` (bearer token via `ABSTRACTGATEWAY_AUTH_TOKEN`/`ABSTRACTGATEWAY_AUTH_TOKENS`, user principals via `ABSTRACTGATEWAY_USER_AUTH=1` or `ABSTRACTGATEWAY_AUTH_MODE=users`, origin allowlist via `ABSTRACTGATEWAY_ALLOWED_ORIGINS`, request limits). `/api/health` is intentionally public. `abstractgateway serve` fails fast when protected writes have neither a shared token nor user auth configured (`src/abstractgateway/security/gateway_security.py`, startup checks in `src/abstractgateway/cli.py`).
- Local/single-user mode maps the Gateway bearer token to `local-admin`. Hosted user-auth mode resolves bearer tokens to principals, exposes `GET /api/gateway/me`, provides admin-only `/api/gateway/admin/users` CRUD plus retained-runtime lifecycle routes under `/api/gateway/admin/runtime-reservations`, stores user token hashes under `<ABSTRACTGATEWAY_DATA_DIR>/auth/users.json` by default, rejects duplicate runtime ids within the same tenant, reserves retained runtime ids when users are deleted, and routes each principal to a separate GatewayService data plane under `<DATA_DIR>/users/<tenant_id>/<runtime_id>/`.
- Gateway serves a built-in control-plane console at `/console`. It uses Gateway browser sessions, shows the current account/runtime, lets admins manage user records (including optional email metadata), rotate tokens, transfer or purge retained runtime reservations, and lets signed-in users edit capability defaults plus provider endpoint profiles from Gateway-discovered provider/model catalogs without storing bearer tokens in browser storage.
- Browser apps should exchange Gateway user bearer tokens through `POST /api/gateway/session/login`, then use `X-AbstractGateway-Session` plus `X-AbstractGateway-CSRF` for mutating session-authenticated requests. The login response body does not expose the session id or CSRF token. Gateway stores revocable browser sessions under `<ABSTRACTGATEWAY_DATA_DIR>/auth/sessions.json` by default and invalidates them when the backing user is disabled, deleted, or token-rotated.
- Gateway browser session cookies use an HTTP-only session cookie, a separate CSRF cookie, `SameSite=Lax`, path `/`, no `Secure` flag on HTTP, and `Secure` under HTTPS. Non-remembered sessions omit `Max-Age`; remembered sessions include it. Origin checks reject untrusted origins and allow only configured hosted app origins.
- Hosted user-auth mode applies execution-host Core defaults, then the Gateway/root baseline, then per-principal capability-default overlays under each user's runtime data plane. These overlays select provider/model/base URL defaults for the user's runtime without mutating other users. Gateway provider endpoint profiles store reusable endpoint descriptions, base URLs, optional model allowlists, and write-only API keys; discovery exposes them as virtual providers such as `endpoint:office-vllm`, and Runtime receives only transient resolved provider/base URL/key parameters.
- A central Gateway route-family policy keeps operator/admin surfaces, model residency controls, and server-workspace file helpers admin-only. Browser-local files should use upload routes; server workspace read/import/export routes require admin until per-user workspace grants are defined.
- Gateway discovery is permission-aware for those high-trust surfaces: ordinary users see admin-only workspace artifact import/export and provider prompt-cache controls marked unavailable with `admin_required` metadata. Session prompt-cache key hashes include a private principal scope so users do not collide on the same session/provider/model tuple.
- Shared/default workflows use the Gateway workflow catalog (`/api/gateway/workflow-catalog` plus admin-only `/api/gateway/admin/workflow-catalog/*`), not another user's private `/api/gateway/bundles` surface. Catalog versions are immutable, admins move default pointers and ACLs, and users start catalog workflows with explicit `registry_scope: "tenant_catalog"` so execution happens in their own runtime. Gateway signs catalog workflow policy before passing it to Runtime, and private bundle routes reject catalog-internal ids.
- Bundle mode is the default workflow source (`ABSTRACTGATEWAY_WORKFLOW_SOURCE=bundle`) and loads `.flow` bundles from `ABSTRACTGATEWAY_FLOWS_DIR` (`src/abstractgateway/service.py`, `src/abstractgateway/hosts/bundle_host.py`).
- SQLite safety invariant: when `ABSTRACTGATEWAY_STORE_BACKEND=sqlite`, the DB file (`ABSTRACTGATEWAY_DB_PATH`, default `<DATA_DIR>/gateway.sqlite3`) must be under `ABSTRACTGATEWAY_DATA_DIR` (fail-fast; prevents cross-wiring durable state) (`src/abstractgateway/config.py`).
- The system is replay-first; SSE streams are an optimization, not a source of truth (`src/abstractgateway/routes/gateway.py`).
- Gateway LLM helper defaults resolve explicit request values, optional flow pins, and execution-host capability route defaults such as `output.text`. Missing provider/model is a clear config error, not a hardcoded local fallback (`src/abstractgateway/provider_defaults.py`, `src/abstractgateway/capability_defaults.py`).
- Optional operator endpoints include account-scoped email inbox routes (`/api/gateway/email/*`) used by AbstractObserver (Inbox → Email) (`src/abstractgateway/routes/gateway.py`).

Multimodal notes:
- Capability discovery for thin clients: `GET /api/gateway/discovery/capabilities` (best-effort; reports installed optional capabilities like `voice`, `tools`, `visualflow`, `vision_fallback`, `media`, plus the versioned shared run contract including input-data/history-bundle read endpoints, direct media feature gates, voice `tts|stt|listen`, prompt-cache surfaces, and model-residency truth).
- The shared artifact contract includes run artifact listing/content, session-visible artifact listing, cross-run/session/run artifact search, workspace-path import, and artifact-to-workspace export. Browser-local files use upload; Gateway workspace paths use import.
- Direct Gateway voice/audio durable endpoints (when `abstractvoice` is installed on the gateway host):
  - STT: `POST /api/gateway/runs/{run_id}/audio/transcribe`
  - TTS: `POST /api/gateway/runs/{run_id}/voice/tts`
- Direct Gateway image durable endpoints:
  - image generate: `POST /api/gateway/runs/{run_id}/images/generate`
  - image edit: `POST /api/gateway/runs/{run_id}/images/edit`
- Direct Gateway video durable endpoints:
  - text-to-video: `POST /api/gateway/runs/{run_id}/videos/generate`
  - image-to-video: `POST /api/gateway/runs/{run_id}/videos/from_image`
- Direct Gateway music durable endpoint:
  - music: `POST /api/gateway/runs/{run_id}/music/generate`
- Generated and edited images plus text-to-video/image-to-video are available through Runtime workflows and the direct run-scoped routes when the corresponding Runtime/Core vision backend is configured. Video progress is exposed on the returned child run ledger as `abstract.progress`.
- Dynamic catalogs for thin clients are exposed at `GET /api/gateway/voice/voices`, `GET /api/gateway/audio/speech/models`, `GET /api/gateway/audio/transcriptions/models`, `GET /api/gateway/audio/music/providers`, `GET /api/gateway/audio/music/models`, and `GET /api/gateway/vision/provider_models`; they proxy AbstractCore Server catalog routes when configured.
- Catalog routes now add a Gateway-owned stable envelope: `catalog.contract=gateway_catalog_v1`, `catalog.version=1`, and canonical `items`, while still preserving legacy lower-layer fields for compatibility.
- The shared client contract also exposes `common.readiness` as `gateway_surface_readiness_v1`: a compact surface-level summary derived from Gateway descriptors, media gates, memory, prompt-cache, and Runtime-backed model-residency truth.
- Deployment/readiness truth is still narrower than catalog shape; Gateway does not invent selected-backend or degraded-state truth unless Runtime/Core exposes it.
- `voice.listen` is a host-capture contract for higher apps, not a server microphone socket; clients capture locally and then emit an event or upload audio for STT.
- KG memory is selected through Gateway env (`ABSTRACTGATEWAY_MEMORY_STORE_BACKEND=lancedb|memory`, plus `sqlite` when the installed AbstractMemory build exposes `SQLiteTripleStore`) and queried through `POST /api/gateway/kg/query`; store implementations remain owned by AbstractMemory, Gateway targets the compatible AbstractMemory 0.0.x TripleStore API, and fresh empty stores are reported as available when the backend resolves.
- Text embeddings use the execution-host `embedding.text` capability route. Remote/provider-backed embeddings work in the base light install; local HuggingFace/sentence-transformer embeddings require `abstractgateway[embeddings]`. Gateway does not persist a separate embedding provider/model file; split deployments proxy embedding generation to the remote AbstractCore server.
- `/api/gateway/prompt_cache/*` exposes admin-only provider/model prompt-cache controls; `/api/gateway/prompt_cache/saved|save|load` are Runtime-backed host-local export/import admin aliases; `/api/gateway/sessions/{session_id}/prompt_cache/*` adds principal-scoped Gateway-owned session naming/status/prepare/clear/rebuild orchestration over those provider controls.
- Docker scope: `ghcr.io/lpalbou/abstractgateway:latest` is the release-grade lightweight base-package image; `ghcr.io/lpalbou/abstractgateway:gpu-latest` / `:<version>-gpu` installs `abstractgateway[gpu]` and is experimental/best-effort until a CUDA smoke gate exists. The container defaults to `ABSTRACTGATEWAY_USER_AUTH=1`; its entrypoint creates `default/admin` when missing and writes the first-login token under `<ABSTRACTGATEWAY_DATA_DIR>/auth/bootstrap-admin-token`. Apple/MLX inference should run natively on macOS and be reached from the Gateway container via Docker Model Runner (`http://model-runner.docker.internal/engines/v1`) or a host bridge OpenAI-compatible URL.

Regeneration:
- `python scripts/generate-llms-full.py` regenerates `llms-full.txt` from the Markdown doc links in this `llms.txt`.

Compatibility note:
- `CHANGELOD.md` → `CHANGELOG.md`
- `ACKNOWLEDMENTS.md` → `ACKNOWLEDGMENTS.md`

## Ecosystem

- [AbstractFramework](https://github.com/lpalbou/AbstractFramework): ecosystem meta-repo (this gateway is one component)
- [AbstractRuntime](https://github.com/lpalbou/abstractruntime): durable runs + tick loop + stores (required dependency)
- [AbstractCore](https://github.com/lpalbou/abstractcore): lower-level provider/tool/media integrations consumed through Runtime facades
- [AbstractRuntime ↔ AbstractCore integration](./README.md): why bundle LLM/tool nodes use the Runtime/Core integration
- [AbstractFlow](https://github.com/lpalbou/abstractflow): authoring + bundling `.flow` WorkflowBundles (optional)

## Start here

- [README.md](./README.md): install + quickstart + doc map
- [docs/README.md](./docs/README.md): docs index (what to read for each topic)
- [docs/getting-started.md](./docs/getting-started.md): step-by-step setup (bundle mode + file/SQLite + split API/runner)
- [llms-full.txt](./llms-full.txt): single-file snapshot of the Markdown docs linked in this file

## Project docs

- [CHANGELOG.md](./CHANGELOG.md): release notes (human-oriented)
- [CONTRIBUTING.md](./CONTRIBUTING.md): dev setup + tests + contribution workflow
- [SECURITY.md](./SECURITY.md): responsible vulnerability reporting process
- [ACKNOWLEDGMENTS.md](./ACKNOWLEDGMENTS.md): dependency acknowledgments (see `pyproject.toml` for canonical list)

## Core docs

- [docs/getting-started.md](./docs/getting-started.md): run the gateway + choose file vs SQLite stores + split API/runner
- [docs/api.md](./docs/api.md): client contract + curl examples (replay-first ledger + durable commands)
- [docs/security.md](./docs/security.md): token auth + origin allowlist + limits + audit log
- [docs/configuration.md](./docs/configuration.md): env vars + install extras
- [docs/deployment.md](./docs/deployment.md): Docker/GHCR/Compose deployment profile
- [docs/architecture.md](./docs/architecture.md): components + durability model (includes diagram)
- [docs/faq.md](./docs/faq.md): common questions + troubleshooting

## Key code entrypoints

- [pyproject.toml](./pyproject.toml): packaging metadata + dependency extras + CLI entrypoint
- [src/abstractgateway/app.py](./src/abstractgateway/app.py): FastAPI app + middleware + routers + health endpoint
- [src/abstractgateway/routes/gateway.py](./src/abstractgateway/routes/gateway.py): `/api/gateway/*` endpoints (runs, ledger, bundles, commands, tooling)
- [src/abstractgateway/security/gateway_security.py](./src/abstractgateway/security/gateway_security.py): auth/origin/limits/audit log middleware
- [src/abstractgateway/service.py](./src/abstractgateway/service.py): composition root (config + stores + host + runner + optional bridges)
- [src/abstractgateway/runner.py](./src/abstractgateway/runner.py): durable command inbox polling + run ticking
- [src/abstractgateway/stores.py](./src/abstractgateway/stores.py): file vs SQLite store wiring
- [src/abstractgateway/config.py](./src/abstractgateway/config.py): env var parsing + sqlite safety invariant
- [src/abstractgateway/cli.py](./src/abstractgateway/cli.py): `abstractgateway serve|runner|migrate|triage-*|...`
- [src/abstractgateway/hosts/bundle_host.py](./src/abstractgateway/hosts/bundle_host.py): `.flow` bundle loading + VisualFlow compilation + tool/LLM wiring switches
- VisualFlow directory mode was intentionally removed; use VisualFlow CRUD + publish to `.flow` bundles (`/api/gateway/visualflows/*`) and run in bundle mode.

## Optional

- [docs/maintenance.md](./docs/maintenance.md): operator tooling (triage/backlog/process manager/file helpers; high trust required)
- [scripts/generate-llms-full.py](./scripts/generate-llms-full.py): generator for `llms-full.txt`
- [tests/](./tests/): test suite (`pytest`, most tests are marked `basic`)
