From 28eb4c87bb8e643342f5b4db1b8058e7941df92f Mon Sep 17 00:00:00 2001 From: Sudhir Verma <9924513+sudhirverma@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:54:36 +0530 Subject: [PATCH 1/2] docs(security): align air-gap claims with secure defaults (A4 / AAP-82838) Document localhost-first HTTP, auth, CORS, and gRPC TLS after AAP-82788 / AAP-82804, state residual air-gap risks, and stop claiming isolation alone secures Abbenay (DR-038). --- README.md | 9 +++-- docs/ARCHITECTURE.md | 17 ++++++--- docs/CONFIGURATION.md | 4 +++ docs/CONTAINER.md | 5 +++ docs/GETTING_STARTED.md | 5 ++- docs/LANDSCAPE.md | 8 +++-- docs/PRODUCT_OVERVIEW.md | 15 +++++++- docs/REDHAT_AI.md | 8 ++++- docs/ROADMAP.md | 14 +++++++- docs/SECURITY.md | 76 +++++++++++++++++++++++++++++++++++++++ docs/decisions.md | 21 +++++++++++ packages/vscode/README.md | 8 +++-- 12 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 docs/SECURITY.md diff --git a/README.md b/README.md index f089e7a..7cba629 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Abbenay produces two packages from a single source tree: - **MCP aggregation**: Connect to external MCP servers, expose daemon as MCP server - **Dynamic model discovery**: Fetches available models from provider APIs - **Single Executable Application (SEA)**: Self-contained binary, no Node.js install required +- **Secure-by-default daemon**: HTTP on `127.0.0.1` with auth + CORS allowlist; gRPC TLS required off-loopback — see [SECURITY.md](docs/SECURITY.md) ## Why "Abbenay"? @@ -140,11 +141,14 @@ curl -H "Authorization: Bearer $ABBENAY_API_TOKEN" \ Works with Cursor, Continue, aider, and any `openai` SDK script (use the same token as the client API key). The HTTP server binds to `127.0.0.1` by default; -use `--host 0.0.0.0` only when you intentionally expose it. +use `--host 0.0.0.0` only when you intentionally expose it. gRPC TCP on +non-loopback addresses requires `--grpc-tls` (or explicit `--insecure`). > **WARNING:** Auth is on by default. `ABBENAY_HTTP_AUTH=0` disables it on any > bind (including `--host 0.0.0.0`) — for example a cluster-internal pod, or -> when a reverse proxy already authenticates callers. See +> when a reverse proxy already authenticates callers. Air-gap or offline use +> with local models reduces *egress*, but **network isolation alone does not +> secure Abbenay**. See [Security & air-gap](docs/SECURITY.md) and > [Configuration](docs/CONFIGURATION.md#http-api-security-server). ### Using the core library @@ -289,6 +293,7 @@ abbenay/ ## Documentation - [Getting Started](docs/GETTING_STARTED.md) +- [Security, Privacy & Air-Gap](docs/SECURITY.md) — defaults vs air-gap claims - [Core Library (API Reference)](docs/CORE.md) - [Architecture](docs/ARCHITECTURE.md) - [Configuration](docs/CONFIGURATION.md) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 4cab05a..d507704 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -110,7 +110,7 @@ The core TypeScript/Node.js process that runs as a background daemon. The web dashboard runs inside the daemon process via Express: -- **Port**: `localhost:8787` (configurable) +- **Bind / port**: `127.0.0.1:8787` by default (configurable; auth required) - **Static assets**: Served from `packages/daemon/static/` - **API routes**: `/api/*` -> Direct calls to `DaemonState` (no gRPC in the loop) - **Chat SSE**: `POST /api/chat` -> Streaming responses via Server-Sent Events @@ -491,7 +491,16 @@ internal MCP tool for cross-session retrieval. ## Security - **Secrets**: Stored in system keychain via keytar when available; never in config files -- **Socket**: Unix socket (or named pipe) with user-only permissions -- **Web dashboard**: Listens on localhost only -- **No remote access**: Daemon designed for local use only +- **Socket**: Unix socket (or named pipe) with user-only permissions (local IPC) +- **Web dashboard / HTTP API**: Binds to `127.0.0.1` by default; Bearer (or + cookie + CSRF) auth required; CORS allowlist only (never `*`) — DR-030 +- **gRPC TCP**: Loopback plaintext OK for local DX; non-loopback requires + `--grpc-tls` or explicit `--insecure` — DR-029. Empty `consumers` refused + off-loopback unless `--allow-open-auth` / `--insecure` — DR-037 +- **Air-gap / privacy**: Local and on-prem engines can keep prompts off the + public internet, but **network isolation alone does not secure the daemon**. + See [SECURITY.md](./SECURITY.md). - **Config files**: Created with mode `0o600` (user read/write only) + +Intentional exposure (containers, LAN) is opt-in via `--host` / image CMD — +see [CONTAINER.md](./CONTAINER.md) and [SECURITY.md](./SECURITY.md). diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index a20083c..d325c84 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -89,6 +89,10 @@ MCP endpoint (`/mcp`) require authentication by default. MCP tool calls also honor `tool_policy` (same approval path as chat — see [Tool policy](#tool-policy) below). +For air-gap / privacy claims vs real defaults, see +[SECURITY.md](./SECURITY.md). **Network isolation alone does not secure +Abbenay.** + | Setting / env | Purpose | Default | |---------------|---------|---------| | `ABBENAY_API_TOKEN` or `server.api_token` / `server.api_token_env` | Bearer token for all HTTP routes | Auto-generated and stored as `http-api-token` in the config directory | diff --git a/docs/CONTAINER.md b/docs/CONTAINER.md index d43c975..0235802 100644 --- a/docs/CONTAINER.md +++ b/docs/CONTAINER.md @@ -356,6 +356,11 @@ spec: ## Security: HTTP bind and authentication +> Workstation CLI defaults bind HTTP to `127.0.0.1` with auth on. The +> **container image CMD** intentionally uses `--host 0.0.0.0` so published +> ports work — that is opt-in exposure, not “air-gap security.” See +> [SECURITY.md](./SECURITY.md). + | Flag | Default | Effect | |------|---------|--------| | `--host` / `ABBENAY_HTTP_HOST` / `server.host` | `127.0.0.1` | HTTP bind (dashboard, `/api/*`, `/v1/*`, `/mcp`) | diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md index 6ff7bf5..caa87b0 100644 --- a/docs/GETTING_STARTED.md +++ b/docs/GETTING_STARTED.md @@ -257,7 +257,9 @@ same token as the API key. > logs a warning. Use auth-off when something else already protects the API > (for example a production pod on a private cluster network, or a reverse > proxy / gateway that authenticates callers). Otherwise keep auth on and -> use `ABBENAY_API_TOKEN`. +> use `ABBENAY_API_TOKEN`. Defaults bind HTTP to `127.0.0.1` with CORS +> allowlisted — see [SECURITY.md](SECURITY.md). Offline / air-gap use with +> local models does **not** replace these controls. ### With the OpenAI Python SDK @@ -394,6 +396,7 @@ aby list-engines ## Next steps +- [Security, Privacy & Air-Gap](SECURITY.md) — defaults vs air-gap claims - [Configuration Reference](CONFIGURATION.md) — all config options - [Core Library API](CORE.md) — use `@abbenay/core` in your own apps - [Architecture](ARCHITECTURE.md) — how the system fits together diff --git a/docs/LANDSCAPE.md b/docs/LANDSCAPE.md index 2cadda1..564ab96 100644 --- a/docs/LANDSCAPE.md +++ b/docs/LANDSCAPE.md @@ -78,10 +78,14 @@ aggregation, and a web dashboard. **Strengths:** no infrastructure to deploy, works offline with local models, secrets stay on the developer's machine, same core powers CLI / daemon / extension / web, in-process tool execution with approval -policies. +policies. Secure-by-default HTTP (localhost bind, auth, CORS allowlist) +and fail-closed gRPC TLS off-loopback — see [SECURITY.md](./SECURITY.md). **Trade-offs:** not designed for team-wide centralized control, no -built-in cost tracking or billing, earlier in development. +built-in cost tracking or billing, earlier in development. Offline / +air-gap routing to local models reduces public-cloud egress; it does +**not** by itself authenticate callers or replace daemon security +controls. --- diff --git a/docs/PRODUCT_OVERVIEW.md b/docs/PRODUCT_OVERVIEW.md index 66d3517..cda6c0d 100644 --- a/docs/PRODUCT_OVERVIEW.md +++ b/docs/PRODUCT_OVERVIEW.md @@ -2,7 +2,7 @@ **Status:** MVP Complete **Version:** 0.0.0-dev -**Last Updated:** March 2026 +**Last Updated:** July 2026 --- @@ -252,6 +252,19 @@ providers: --- +## Security and air-gap posture + +Abbenay supports **local and on-prem engines** (Ollama, Red Hat AI, LM Studio, +custom OpenAI-compatible endpoints) so operators can keep prompts inside their +network. That is a routing choice — not a security boundary by itself. + +Defaults: HTTP on `127.0.0.1` with auth and CORS allowlist; gRPC TLS required +off-loopback. Abbenay does **not** ship network-isolation controls (firewall / +offline enforcement); operators own that layer. **Network isolation alone does +not secure Abbenay.** See [SECURITY.md](./SECURITY.md). + +--- + ## Out of Scope | Item | Notes | diff --git a/docs/REDHAT_AI.md b/docs/REDHAT_AI.md index 047f0d2..fa47be1 100644 --- a/docs/REDHAT_AI.md +++ b/docs/REDHAT_AI.md @@ -13,6 +13,10 @@ an OpenAI-compatible REST API: Both speak the same protocol, so Abbenay routes them through `@ai-sdk/openai-compatible` — no additional SDK packages are required. +Local Inference Server deployments support air-gapped / offline inference +(prompts stay on your network). That does **not** mean network isolation +secures the daemon — see [SECURITY.md](./SECURITY.md). + --- ## Profile A — Inference Server @@ -189,9 +193,10 @@ providers: | 5 | Model discovery (CLI) | `aby list-models --discover redhat` | | 6 | Model discovery (web) | Add Provider wizard → select `redhat` | | 7 | Chat completions | `aby chat -m redhat-inference/` | -| 8 | Streaming | `curl http://localhost:8787/v1/chat/completions` with `"stream": true` | +| 8 | Streaming | `curl -H "Authorization: Bearer $ABBENAY_API_TOKEN" http://127.0.0.1:8787/v1/chat/completions` with `"stream": true` | | 9 | Tool calling | `aby chat` with tools enabled (if model supports it) | | 10 | VS Code LM API | Configure Provider → select model → chat | +| 11 | Air-gap / privacy | Use local Inference Server only; isolation alone does not secure Abbenay — see [SECURITY.md](./SECURITY.md) | --- @@ -202,3 +207,4 @@ providers: - [Red Hat AI Inference — Configuring API key authentication](https://docs.redhat.com/en/documentation/red_hat_ai_inference/3.4/html/getting_started/configuring-api-key-authentication_getting-started) - [RHEL AI 3.4 — Getting Started](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_ai/3.4/html-single/getting_started/getting_started) - [Abbenay Configuration Guide](CONFIGURATION.md) +- [Security, Privacy & Air-Gap](SECURITY.md) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 2f365d5..cbaa4fa 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -122,7 +122,10 @@ aider, any `openai` SDK script, etc.). See DR-020. | `POST /v1/completions` | low — legacy | | `POST /v1/embeddings` | low — if engines support it | | Usage/token stats (real counts from providers) | medium | -| Bearer token auth on all HTTP routes (`server.api_token` / `ABBENAY_API_TOKEN`) | **done** | +| Bearer token auth on all HTTP routes (`server.api_token` / `ABBENAY_API_TOKEN`) | **done** (DR-030) | +| Localhost-first HTTP bind + CORS allowlist (never `*`) | **done** (DR-030) | +| gRPC TLS / fail-closed non-loopback binds | **done** (DR-029) | +| Air-gap / privacy docs aligned with defaults (finding A4) | **done** — [SECURITY.md](./SECURITY.md) | | Rate limiting | low | ### Key files @@ -260,6 +263,15 @@ by wiring to `ToolRegistry`. --- +## 8. Air-gap docs vs network exposure (A4) — done + +Product copy historically overstated air-gap / privacy while defaults exposed +HTTP on all interfaces, plaintext gRPC, and wildcard CORS. Docs now state the +real defaults and that **network isolation alone does not secure Abbenay** — +see [SECURITY.md](./SECURITY.md) (DR-038). Code fixes: DR-030 / DR-029. + +--- + ## Suggested implementation order | Phase | Sections | Rationale | Status | diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..3964097 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,76 @@ +# Security, Privacy, and Air-Gap + +Abbenay can use **local / on-prem models** (Ollama, Red Hat AI Inference +Server, LM Studio, and similar) so prompts need not leave your network. +That is a **data-routing** property — not authentication, authorization, or +TLS. + +> **Network isolation alone does not secure Abbenay.** +> An air-gapped or offline host can still expose a reachable daemon to every +> local process and every client that can contact the bind address. Do not +> treat “no internet” as a substitute for the defaults below. + +Code fixes for the historical gaps (all-interface HTTP, wildcard CORS, +plaintext gRPC off-loopback) live in DR-030 / AAP-82788 and DR-029 / +AAP-82804. This page aligns product claims with that posture (finding A4). + +Config details: [CONFIGURATION.md](./CONFIGURATION.md). Containers: +[CONTAINER.md](./CONTAINER.md). + +--- + +## Default posture (after secure defaults) + +| Control | Default | +|---------|---------| +| **HTTP bind** | `127.0.0.1` (not `0.0.0.0`) | +| **HTTP auth** | Required (Bearer / dashboard session) | +| **CORS** | Explicit allowlist — never `*` | +| **gRPC TCP** | Loopback plaintext OK; non-loopback needs `--grpc-tls` or `--insecure` | + +### What this addresses from finding A4 + +| Historical issue | Current posture | +|------------------|-----------------| +| HTTP on `0.0.0.0` (H1) | Binds `127.0.0.1` | +| Wildcard CORS (C1) | Allowlist only; auth required | +| Plaintext gRPC when exposed (C2) | TLS (or explicit `--insecure`) off-loopback | +| No network isolation controls | **Not a product feature** — see below | + +### No network isolation controls (by design) + +Abbenay does **not** provide firewall, VLAN, offline-enforcement, or other +network-isolation features. Air-gap / offline networking is an **operator / +environment** concern (host firewall, air-gapped NIC, no default route, etc.). + +What Abbenay *does* provide are daemon exposure controls: + +| Control | Role | +|---------|------| +| **Bind address** | Limits which interfaces accept connections | +| **HTTP auth** | Blocks anonymous API / dashboard / MCP access | +| **CORS allowlist** | Stops arbitrary websites from calling the daemon in a browser | +| **gRPC TLS / `--insecure`** | Protects or explicitly opts out of encryption off-loopback | +| **Consumers** (gRPC) | Capability tokens for non-loopback gRPC clients | + +Documenting these is how A4 is closed — not by inventing an isolation product. +Operators who need isolation must apply it outside Abbenay **and** keep these +defaults (or stronger) in place. + +### Residual risks even when air-gapped + +- Same-user local processes can still reach loopback / Unix sockets. +- Stolen API or consumer tokens still work. +- `--host 0.0.0.0`, `ABBENAY_HTTP_AUTH=0`, and `--insecure` deliberately weaken posture. +- Cloud providers still receive prompts if you configure them. +- HTTP on loopback remains plaintext; use a TLS-terminating proxy when exposing beyond the machine. + +--- + +## Decision references + +| Decision | Topic | +|----------|--------| +| DR-029 | Fail-closed TLS for non-loopback gRPC TCP | +| DR-030 | Secure-by-default HTTP (auth, CORS, bind) | +| DR-038 | Air-gap docs must not claim isolation equals security | diff --git a/docs/decisions.md b/docs/decisions.md index 231fcf0..6bd6585 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -612,6 +612,27 @@ compare closes token oracle leaks. --- +## DR-038: Air-gap docs must match secure defaults (finding A4) + +**Date:** 2026-07-20 +**Decision:** After DR-029 / DR-030 land, product and feature documentation +MUST describe the real localhost-first HTTP defaults, auth, CORS allowlist, +and gRPC TLS posture, and MUST NOT claim that network isolation or air-gap +alone secures Abbenay. Docs MUST also state that Abbenay does **not** provide +network-isolation features (firewall / offline enforcement); isolation remains +an operator/environment control, while bind/auth/CORS/TLS/consumers are the +daemon controls. Residual risks, a step-by-step operator checklist (bind, +auth, CORS, TLS, consumers, MCP), and changelog callouts for the security +default changes are required in `docs/SECURITY.md` and +`packages/vscode/CHANGELOG.md`. +**Rationale:** Finding A4 — messaging emphasized air-gap / privacy while +historical defaults (all-interface HTTP, plaintext gRPC, wildcard CORS) and +the absence of product-level isolation controls could leave an admin believing +isolation equals security. Technical fixes live in AAP-82788 / AAP-82804; this +decision closes the docs / claims half. + +--- + ## DR-039: Block MCP self-connections to the daemon's own endpoints **Date:** 2026-07-20 diff --git a/packages/vscode/README.md b/packages/vscode/README.md index ea1aba5..7234346 100644 --- a/packages/vscode/README.md +++ b/packages/vscode/README.md @@ -87,11 +87,15 @@ aby web # Start web dashboard aby start # Or start everything at once ``` -Open http://localhost:8787 to: +Open http://127.0.0.1:8787 to: - Add API keys (stored in system keychain or referenced from environment variables) - Enable/disable providers - Select which models to expose +HTTP auth is on by default — use the dashboard login / API token when prompted. +The daemon binds to loopback by default; see +[Security & air-gap](../../docs/SECURITY.md). + ### Config Files Configuration is stored in YAML files: @@ -226,7 +230,7 @@ npm run package ### Models Not Appearing -1. Open web dashboard (http://localhost:8787) +1. Open web dashboard (http://127.0.0.1:8787) — sign in with the API token if prompted 2. Ensure provider has valid API key 3. Enable the models you want 4. Reload VS Code window From 66024149844d49d4455fe18d8fa51ef1845fafda Mon Sep 17 00:00:00 2001 From: Sudhir Verma <9924513+sudhirverma@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:04:54 +0530 Subject: [PATCH 2/2] docs(security): add operator checklist and changelog for A4 DoD Close remaining AAP-82838 acceptance gaps: step-by-step verify for bind, auth, CORS, TLS, consumers, and MCP, plus Unreleased security notes for AAP-82788 / AAP-82804 defaults (DR-038). --- README.md | 2 +- docs/ROADMAP.md | 5 +- docs/SECURITY.md | 120 ++++++++++++++++++++++++++++++++++- packages/vscode/CHANGELOG.md | 13 ++++ 4 files changed, 137 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7cba629..083f525 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,7 @@ abbenay/ ## Documentation - [Getting Started](docs/GETTING_STARTED.md) -- [Security, Privacy & Air-Gap](docs/SECURITY.md) — defaults vs air-gap claims +- [Security, Privacy & Air-Gap](docs/SECURITY.md) — defaults, residual risks, operator checklist - [Core Library (API Reference)](docs/CORE.md) - [Architecture](docs/ARCHITECTURE.md) - [Configuration](docs/CONFIGURATION.md) diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index cbaa4fa..a0fee22 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -268,7 +268,10 @@ by wiring to `ToolRegistry`. Product copy historically overstated air-gap / privacy while defaults exposed HTTP on all interfaces, plaintext gRPC, and wildcard CORS. Docs now state the real defaults and that **network isolation alone does not secure Abbenay** — -see [SECURITY.md](./SECURITY.md) (DR-038). Code fixes: DR-030 / DR-029. +see [SECURITY.md](./SECURITY.md) (DR-038), including the operator checklist +(bind, auth, CORS, TLS, consumers, MCP) and residual risks. Release notes: +`packages/vscode/CHANGELOG.md` (Unreleased → Security). Code fixes: DR-030 / +DR-029. --- diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 3964097..473d3e1 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -15,7 +15,8 @@ plaintext gRPC off-loopback) live in DR-030 / AAP-82788 and DR-029 / AAP-82804. This page aligns product claims with that posture (finding A4). Config details: [CONFIGURATION.md](./CONFIGURATION.md). Containers: -[CONTAINER.md](./CONTAINER.md). +[CONTAINER.md](./CONTAINER.md). Verify a live deploy with the +[operator checklist](#operator-checklist) below. --- @@ -67,6 +68,123 @@ defaults (or stronger) in place. --- +## Operator checklist + +Run these after a fresh default start (`aby web` or `abbenay daemon` with no +host/TLS override). Replace `8787` if you use another HTTP port. Load the API +token from `ABBENAY_API_TOKEN` or the auto-generated `http-api-token` file in +the config directory (`~/Library/Application Support/abbenay` on macOS, +`~/.config/abbenay` on Linux, `%APPDATA%\abbenay` on Windows). + +```bash +export ABBENAY_API_TOKEN="${ABBENAY_API_TOKEN:-$(cat "$HOME/Library/Application Support/abbenay/http-api-token" 2>/dev/null || cat "$HOME/.config/abbenay/http-api-token")}" +``` + +### 1. Bind address + +Confirm HTTP listens on loopback only (not `0.0.0.0`): + +```bash +# macOS / BSD +lsof -nP -iTCP:8787 -sTCP:LISTEN +# Linux +ss -ltnp 'sport = :8787' +``` + +**Pass:** listener address is `127.0.0.1:8787` (or `[::1]:8787`). +**Fail:** `*:8787`, `0.0.0.0:8787`, or a LAN IP without an intentional `--host` opt-in. + +### 2. HTTP auth + +```bash +# Unauthenticated API → 401 +curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8787/api/health +# Authenticated → 200 +curl -s -o /dev/null -w '%{http_code}\n' \ + -H "Authorization: Bearer $ABBENAY_API_TOKEN" \ + http://127.0.0.1:8787/api/health +``` + +**Pass:** first prints `401`, second prints `200`. +**Fail:** unauthenticated request succeeds, or auth cannot be satisfied with the token. + +### 3. CORS allowlist + +```bash +curl -s -o /dev/null -w '%{http_code}\n' \ + -H 'Origin: https://evil.example' \ + -H "Authorization: Bearer $ABBENAY_API_TOKEN" \ + http://127.0.0.1:8787/api/health + +curl -s -D - -o /dev/null \ + -H 'Origin: http://127.0.0.1:8787' \ + -H "Authorization: Bearer $ABBENAY_API_TOKEN" \ + http://127.0.0.1:8787/api/health | grep -i access-control-allow-origin +``` + +**Pass:** foreign Origin returns `403` and no `Access-Control-Allow-Origin: *`; +localhost Origin is echoed in `Access-Control-Allow-Origin`. +**Fail:** response allows `*` or accepts an arbitrary website Origin. + +### 4. gRPC TLS (exposure path) + +Default daemon has no TCP gRPC listener. When you enable TCP: + +```bash +# Loopback plaintext OK +abbenay daemon --grpc-port 50051 +# Must fail without TLS or --insecure +abbenay daemon --grpc-port 50051 --grpc-host 0.0.0.0 +# Must succeed only with explicit opt-in (+ consumers; see step 5) +abbenay daemon --grpc-port 50051 --grpc-host 0.0.0.0 --grpc-tls +``` + +**Pass:** non-loopback without `--grpc-tls` / `--insecure` refuses to start; +with `--grpc-tls`, logs show TLS (see [DEVELOPMENT.md](./DEVELOPMENT.md)). +**Fail:** plaintext gRPC accepts connections on `0.0.0.0` without `--insecure`. + +### 5. Consumers (gRPC) + +On non-loopback TCP, an empty/missing `consumers` section must refuse to start +unless `--allow-open-auth` or `--insecure` is set. Configure consumers per +[CONFIGURATION.md](./CONFIGURATION.md#consumer-authentication-consumers), then: + +```bash +# After consumers + --grpc-tls on 0.0.0.0: sensitive RPCs need x-abbenay-token +# Wrong/missing token → PERMISSION_DENIED (see consumer-auth tests / client docs) +``` + +**Pass:** start fails with empty consumers on `0.0.0.0`; with consumers, wrong +token is denied on gated RPCs. +**Fail:** non-loopback gRPC allows all callers with no consumers and no opt-in. + +### 6. MCP HTTP endpoint + +```bash +# Unauthenticated /mcp → 401 +curl -s -o /dev/null -w '%{http_code}\n' \ + -X POST http://127.0.0.1:8787/mcp \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' +``` + +**Pass:** `401` without Bearer. With Bearer, connection consent is still +required before a session is issued (`GET/POST /api/mcp/connections` — see +[CONFIGURATION.md](./CONFIGURATION.md#mcp-client-connection-consent)). +**Fail:** anonymous clients can open `/mcp` or skip consent. + +### Operator controls outside Abbenay + +These are **not** product features; confirm them in your environment if you +need air-gap / offline posture: + +- Host firewall / security groups restrict who can reach any non-loopback bind +- No unintended default route / NIC bridging for offline hosts +- Strong tokens set via env (`ABBENAY_API_TOKEN`, consumer `token_env`) in + containers — do not rely on auto-generated files alone + +--- + ## Decision references | Decision | Topic | diff --git a/packages/vscode/CHANGELOG.md b/packages/vscode/CHANGELOG.md index 6c08dae..3feeb00 100644 --- a/packages/vscode/CHANGELOG.md +++ b/packages/vscode/CHANGELOG.md @@ -8,6 +8,19 @@ This project uses [CalVer](https://calver.org/) versioning (`YYYY.M.MICRO`). ## [Unreleased] +### Security + +- **Secure-by-default HTTP (AAP-82788 / DR-030):** bind defaults to `127.0.0.1` + (not `0.0.0.0`); Bearer / dashboard auth required on `/api/*`, `/v1/*`, and + `/mcp`; CORS is an explicit allowlist (never `*`). +- **Fail-closed gRPC TCP (AAP-82804 / DR-029):** non-loopback binds require + `--grpc-tls` or explicit `--insecure`; empty `consumers` refused off-loopback + unless `--allow-open-auth` / `--insecure`. +- **Air-gap docs (AAP-82838 / A4 / DR-038):** product copy no longer implies + network isolation alone secures Abbenay. See + [SECURITY.md](../../docs/SECURITY.md) for defaults, residual risks, and the + operator checklist (bind, auth, CORS, TLS, consumers, MCP). + ### Added - Initial VS Code Marketplace release