mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 02:44:59 +00:00
599 lines
28 KiB
Markdown
599 lines
28 KiB
Markdown
# Veritas Kanban Server - Security Guide
|
|
|
|
## Overview
|
|
|
|
The Veritas Kanban server includes a flexible authentication and authorization system to protect API endpoints and WebSocket connections from unauthorized access.
|
|
|
|
## Quick Start
|
|
|
|
### Development (Localhost Bypass)
|
|
|
|
For local development, enable localhost bypass:
|
|
|
|
```bash
|
|
# .env
|
|
VERITAS_AUTH_ENABLED=true
|
|
VERITAS_AUTH_LOCALHOST_BYPASS=true
|
|
```
|
|
|
|
This allows unauthenticated requests from `localhost`/`127.0.0.1` while still requiring auth for remote connections during development.
|
|
In `NODE_ENV=production`, localhost bypass is not honored for HTTP or WebSocket
|
|
auth, even if an old `.env` file still enables it.
|
|
|
|
### Production
|
|
|
|
For production, configure API keys:
|
|
|
|
```bash
|
|
# .env
|
|
VERITAS_AUTH_ENABLED=true
|
|
VERITAS_AUTH_LOCALHOST_BYPASS=false
|
|
VERITAS_ADMIN_KEY=your-secure-admin-key
|
|
VERITAS_API_KEYS=agent1:key1:agent,dashboard:key2:read-only
|
|
```
|
|
|
|
### Remote/Server Mode
|
|
|
|
Remote access must follow the v5 remote security posture in
|
|
[ADR 0002](architecture/ADR-0002-v5-remote-server-security-posture.md). In
|
|
short: prefer one trusted origin for the web client, `/api`, and `/ws`; keep
|
|
auth enabled; disable localhost bypass outside loopback; use HTTPS, VPN, or a
|
|
trusted tunnel for browser/mobile sessions; and use exact origins instead of
|
|
wildcard CORS.
|
|
|
|
## Authentication Methods
|
|
|
|
Clients can authenticate using any of these methods:
|
|
|
|
### 1. Authorization Header (Recommended)
|
|
|
|
```bash
|
|
curl -H "Authorization: Bearer your-api-key" \
|
|
http://localhost:3001/api/tasks
|
|
```
|
|
|
|
### 2. X-API-Key Header
|
|
|
|
```bash
|
|
curl -H "X-API-Key: your-api-key" \
|
|
http://localhost:3001/api/tasks
|
|
```
|
|
|
|
### 3. Query Parameter (WebSocket)
|
|
|
|
```javascript
|
|
const ws = new WebSocket('ws://localhost:3001/ws?api_key=your-api-key');
|
|
```
|
|
|
|
HTTP requests do not accept API keys in query strings. Use headers for HTTP and
|
|
reserve the WebSocket `api_key` query fallback for clients that cannot send auth
|
|
headers during the upgrade.
|
|
|
|
## Roles and Permissions
|
|
|
|
> v5 planning note: the current role model is intentionally small. The planned
|
|
> multi-user model expands this into workspace-scoped `owner`, `admin`,
|
|
> `member`, `reviewer`, `read-only`, and `agent` roles with scoped agent tokens.
|
|
> See [v5 Identity, Workspace, and RBAC Model](IDENTITY-RBAC.md).
|
|
|
|
| Role | Read | Write | Admin Actions |
|
|
| ----------- | ---- | ----- | ------------- |
|
|
| `admin` | ✅ | ✅ | ✅ |
|
|
| `agent` | ✅ | ✅ | ❌ |
|
|
| `read-only` | ✅ | ❌ | ❌ |
|
|
|
|
### Role Details
|
|
|
|
- **admin**: Full access to all endpoints including sensitive operations
|
|
- **agent**: Can read/write tasks, run agents, manage worktrees. Intended for AI agents like [OpenClaw](https://github.com/openclaw/openclaw)
|
|
- **read-only**: Can perform read endpoints, including documented read-like POST
|
|
checks. Suitable for dashboards and monitoring
|
|
|
|
Agent self-service routes are still permission-scoped. Read-like checks such as
|
|
agent routing and permission checks require `agent:read`; approval requests
|
|
require `task:write`; approval review, routing configuration, and permission
|
|
elevation require `admin:manage`.
|
|
|
|
Worktree mutations are fail-closed around repository ownership. Creation
|
|
records a credential-redacted repository fingerprint, exact base commit,
|
|
unique path/branch, and ownership lease before running `git worktree add`.
|
|
Active attempts lock rebase, integration, and cleanup. Cleanup inspects tracked
|
|
changes, untracked files, commits not reachable from the remote base, merge
|
|
reachability, and external process holds. An unavailable hold probe is treated
|
|
as incomplete evidence, not a clean result. Overrideable findings require
|
|
`admin:manage` plus an explicit actor-attributed reason in the durable manifest;
|
|
active runs, unexpired attempt leases, branch mismatches, and manifest
|
|
mismatches cannot be overridden. Integration resume revalidates path
|
|
containment, registered-worktree identity, common Git directory, and
|
|
credential-redacted remote identity before any push. Integration uses a
|
|
detached temporary worktree and a non-force push, so the configured primary
|
|
checkout is not mutated.
|
|
|
|
Repository-controlled execution is a separate trust boundary from worktree
|
|
ownership. Before an executable provider launch, Veritas inventories recognized
|
|
agent instructions, provider configuration, MCP servers, hooks, language-server
|
|
settings, workflows, extensions, skills, and agent definitions. Executable
|
|
configuration requires an explicit, actor-attributed decision for the exact
|
|
workspace identity and inventory digest. Model-only instructions can run
|
|
provisionally only under the enforced restricted profile. The inventory and
|
|
decision are bound into the immutable launch manifest and rescanned before
|
|
provider creation; drift fails closed. A repository-owned policy can only
|
|
narrow trust. See
|
|
[Workspace Execution Trust](architecture/WORKSPACE-EXECUTION-TRUST.md).
|
|
|
|
### v5 Auth Context
|
|
|
|
Authenticated REST requests and WebSocket connections now carry a shared auth
|
|
context for the v5 RBAC migration:
|
|
|
|
| Field | Description |
|
|
| ------------- | ------------------------------------------------------------------------- |
|
|
| `role` | Current compatibility role: `admin`, `agent`, `read-only` |
|
|
| `userId` | Local fallback user ID until persisted users are enforced |
|
|
| `workspaceId` | Local fallback workspace ID until workspace scoping lands |
|
|
| `actorType` | `user`, `agent`, `service`, or `localhost-bypass` |
|
|
| `authMethod` | `disabled`, `session`, `api-key`, `device-session`, or `localhost-bypass` |
|
|
| `tokenName` | API key name when authenticated with a configured key |
|
|
| `permissions` | Role-derived permission list used by new route guards |
|
|
|
|
New v5 endpoints should prefer explicit permission guards over broad role
|
|
checks. Legacy role guards remain supported while route coverage is migrated.
|
|
|
|
Browser password sessions are local-owner only in v5 GA. The server accepts the
|
|
session cookie only on loopback requests with loopback `Host`/`Origin`/`Referer`
|
|
metadata. A verified loopback owner session receives the narrow
|
|
`local-agent:run` capability so the packaged desktop can start and control local
|
|
agents; this does not enable unauthenticated localhost bypass, which remains
|
|
disabled in production. Remote, server, PWA, and multi-user clients must
|
|
authenticate with a trusted device session or scoped API token so active
|
|
workspace membership, role, revocation, and downgraded scopes are revalidated.
|
|
|
|
The v5 authority surface is tracked in
|
|
[`docs/security/permission-coverage.json`](security/permission-coverage.json).
|
|
Run `node scripts/check-permission-coverage.mjs` to fail when a REST route
|
|
prefix, WebSocket event, CLI command, MCP tool, workflow step/action type,
|
|
command palette action, or tracked background job is added without a permission
|
|
classification.
|
|
|
|
The v5.0 hardening review is recorded in
|
|
[`docs/security/v5-security-review.md`](security/v5-security-review.md),
|
|
including fixed high/critical findings, accepted hardening risks, and the
|
|
password-session local-owner boundary.
|
|
|
|
Release compatibility, stale-client behavior, update channels, and rollback
|
|
limits are tracked in
|
|
[`docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md`](V6-COMPATIBILITY-AND-RELEASE-POLICY.md).
|
|
Compatibility errors and debug bundles must redact tokens, cookies, private
|
|
keys, local private paths, raw chat content, and task body text.
|
|
|
|
Buzz communication diagnostics and delivery store environment-variable
|
|
references and public/redacted metadata only. The Nostr private key and
|
|
optional NIP-OA auth tag are resolved only for an active probe or signing
|
|
operation and are never returned.
|
|
The built-in signer accepts hexadecimal or `nsec` private-key material,
|
|
constructs the Buzz-required nonce and exact request-body hash, and clears its
|
|
decoded key bytes after signing on a best-effort basis.
|
|
Relay URLs reject userinfo, query strings, and fragments. Outbound requests use
|
|
scheme validation, explicit plaintext/local/RFC1918/ULA opt-ins, DNS pinning,
|
|
manual redirects, fixed timeouts, and bounded response reads. Link-local,
|
|
cloud-metadata, and CGNAT ranges remain blocked under the private-network
|
|
opt-in. The compatibility probe reads NIP-11 metadata and authenticated query
|
|
filters only. The communication worker uses a DNS-pinned, channel-allowlisted
|
|
NIP-42 WebSocket subscription and verifies event signatures, kinds, channel
|
|
IDs, timestamps, and bounded sizes before projection. Outbound signed events
|
|
are persisted before submission. Ambiguous writes are queried by event ID and
|
|
the persisted signature, configured identity, and mapped channel are
|
|
re-verified before any resubmission. Echoed adapter event IDs are suppressed.
|
|
Optional command discovery runs without a shell and receives a minimal
|
|
environment that excludes provider and Buzz credentials. See
|
|
[Buzz Communication Adapter](BUZZ-INTEGRATION.md).
|
|
|
|
Buzz persona/team import accepts only reconstructed, signature-verified kinds
|
|
`30175` and `30176` from the configured signed query path. Event, content,
|
|
JSON depth, key count, array count, string, URL, and batch limits apply before
|
|
preview. Secret-like values and fields for credentials, environment, commands,
|
|
paths, processes, managed agents, MCP, hooks, skills, or engrams reject the
|
|
record. Public avatar URLs are validated but never fetched.
|
|
|
|
Definition import is an explicit one-way data operation. Newly materialized
|
|
profiles, rosters, and roster members are disabled. Declared Buzz runtime,
|
|
model, and provider values remain source metadata and are not provider-runtime
|
|
evidence. The source event ID and optimistic local revision are checked again
|
|
inside the authoritative JSON-file lock or SQLite transaction before mutation.
|
|
Source removal never deletes local materializations.
|
|
|
|
## Run-scoped Tool Control Plane
|
|
|
|
Tool-server definitions are administrator-controlled and schema validated.
|
|
Transport arguments are arrays executed without a shell, HTTP URLs cannot
|
|
contain userinfo, query strings, or fragments, and environment/header fields
|
|
store key names only. Raw environment and credential values are excluded from
|
|
definitions, discovery records, run catalogs, launch manifests, logs, and
|
|
events. Credential-like environment keys, explicit broker references, and HTTP
|
|
header references fail closed until brokered provider launch handles are
|
|
available.
|
|
|
|
Discovery schemas and all tool inputs and results have bounded byte and item
|
|
limits. Discovery is bound to the exact definition digest so identity or
|
|
version drift invalidates cached evidence. Required failures block launch;
|
|
optional failures remain visible as degraded catalog entries. An immutable
|
|
catalog binds task, attempt, provider-runtime digest, task-envelope digest,
|
|
definition digest, discovery digest, and each per-tool policy decision.
|
|
|
|
Native provider configuration exposes only `allow` decisions. Denied and
|
|
approval-required tools remain disabled. The mediated call route requires
|
|
`agent:write`, the exact active running attempt, and a catalog digest matching
|
|
the persisted launch manifest. Approval-required calls bind the server, tool,
|
|
arguments, catalog digest, and operation ID to `run-approval/v1`; a changed,
|
|
expired, rejected, or stale decision fails closed.
|
|
|
|
Each invocation validates arguments against the discovered JSON Schema,
|
|
dispatches once per stable operation ID, and records bounded redacted
|
|
`tool.started`, `tool.completed`, or causal `run.error` events. Run completion
|
|
closes supervised bridge sessions. Credential-bound definitions remain
|
|
unavailable until the provider launch credential broker can resolve run-scoped
|
|
handles without persisting values.
|
|
|
|
See [Tool Control Plane v1](architecture/TOOL-CONTROL-PLANE-V1.md).
|
|
|
|
## Provider Runtime Capability Enforcement
|
|
|
|
Provider runtime manifests are authorization evidence, not display metadata.
|
|
The server validates their complete capability inventory, canonical SHA-256
|
|
digest, probe state, and secret redaction before use. Launch requirements and
|
|
run controls qualify only with `supported` or `advisory` evidence;
|
|
`unsupported`, `unknown`, missing, failed-probe, malformed, or invalid-digest
|
|
evidence fails closed.
|
|
|
|
The selected manifest is persisted on the attempt before provider execution.
|
|
Status, logs, completion, stop, message/steer, token reporting, tool events, and
|
|
artifact ingestion compare the active snapshot with the persisted digest before
|
|
acting. A mismatch stops provider event ingestion and requires the operator to
|
|
terminate the detached provider through its host supervisor, reconcile attempt
|
|
state, and launch a fresh run. Veritas does not offer a UI force-stop that
|
|
bypasses runtime evidence.
|
|
|
|
The durable run supervisor also binds the provider snapshot to the exact task
|
|
envelope, launch manifest, worktree fingerprint and ownership lease, host
|
|
identity, and operating-system process start identity. Recovery uses an
|
|
expiring compare-and-set lease. Process-group stop is refused when the PID has
|
|
exited, been reused, moved hosts, or cannot be identified safely; remote
|
|
sessions are contacted only through adapter-declared recovery controls.
|
|
|
|
Public sandbox dry-runs accept a live registered manifest digest, not a
|
|
caller-supplied manifest body. The server resolves the digest from current host
|
|
registrations and rejects unknown, expired, or provider-mismatched evidence.
|
|
Human Veritas approval gates remain separate from provider-native
|
|
`run.approvals`; one does not imply the other. Shared co-drive links are pinned
|
|
to their source attempt. Message and approval actions require that exact attempt
|
|
to remain active and require current `run.steer` or `run.approvals` evidence, so
|
|
an old link cannot control a replacement run on the same task.
|
|
|
|
## Agent Sandbox Policies
|
|
|
|
Agent sandbox policy presets live in the shared app config and are managed from
|
|
**Settings -> Agents -> Sandbox Policies** or `/api/sandbox-policies`.
|
|
|
|
Use them to constrain:
|
|
|
|
- Filesystem read/write paths, denied paths, dotfile masking, and local-only handles.
|
|
- Network egress defaults, allowlisted hosts and path prefixes, and private network or metadata endpoint blocks.
|
|
- Environment variable passthrough.
|
|
- Credential access mode: none, brokered references, or explicit environment passthrough.
|
|
|
|
Launch-time validation resolves every preset rule from the persisted provider
|
|
runtime manifest. Required unsupported controls block the agent or workflow
|
|
step before execution. Advisory unsupported controls continue with warnings.
|
|
Every dry-run and launch-time decision writes a governance trace with raw
|
|
detail redacted; credential references and environment-style `name=value`
|
|
strings are shown as `[redacted]`.
|
|
|
|
For local ACP, Claude Code, Codex app-server, Codex CLI, and Hermes runs,
|
|
Veritas can compile those filesystem rules into a version-bound `codex
|
|
sandbox` process wrapper. The credential-free backend probe covers read,
|
|
write, deny, symlink and hard-link escape, dotfile, protected metadata,
|
|
descendant, PATH-tool, and backend re-execution behavior. A failed or missing
|
|
backend blocks a required policy before provider process creation. Codex SDK
|
|
and remote OpenClaw runs require exact provider-native conformance evidence,
|
|
including descendant inheritance, run-scoped temporary storage, and cleanup;
|
|
coarse provider sandbox modes remain advisory.
|
|
|
|
Workspace and home aliases are canonical-base constrained. Nested mounts below
|
|
allowed roots are denied unless explicitly granted or denied, and the relevant
|
|
mount topology is rechecked before provider spawn. A changed or uninspectable
|
|
topology fails closed. The bounded workspace tree is scanned before
|
|
compilation and immediately before activation for pre-existing hard links that
|
|
alias inaccessible external inodes.
|
|
|
|
CLI package and virtual-environment roots are read-only. Linked-worktree Git
|
|
metadata is resolved to exact protected read-only roots so status and diff can
|
|
work without exposing the primary checkout for writes. Ambient system/global
|
|
Git configuration is disabled; only effective author identity values may be
|
|
carried in memory for the run, and they are excluded from manifests and logs.
|
|
The `.git`, `.agents`, `.codex`, and `.veritas-kanban` names receive explicit
|
|
read-only entries directly beneath writable roots and cannot themselves be
|
|
selected as writable policy roots. A protected path that is or becomes a
|
|
symlink blocks launch.
|
|
|
|
Launch evidence stores only canonical path hashes and binds the filesystem
|
|
decision to the exact provider runtime manifest digest and conformed backend
|
|
executable-content digest. The executable is rehashed after policy evaluation
|
|
and immediately before activation, so same-size replacement with restored
|
|
timestamps still blocks launch. Local run-specific temporary and cache
|
|
directories are registered with the durable run supervisor and cleaned on
|
|
terminal completion or recovery; cleanup rejects symlinked ancestors. Remote
|
|
provider-native backends must prove their equivalent lifecycle. See
|
|
[Run-scoped filesystem sandbox backends](architecture/FILESYSTEM-SANDBOX-BACKENDS.md).
|
|
|
|
Required filesystem boundaries have no per-run bypass. Task-readiness
|
|
`overrideReason` values do not affect sandbox enforcement. Relaxation requires
|
|
an authorized advisory policy preset, and the evaluated policy plus launch
|
|
decision are retained as governance evidence.
|
|
|
|
### Credential broker core
|
|
|
|
Credential definitions are stored separately from secret values. An admin can
|
|
register a source reference and bounded host/tool/destination/action policy at
|
|
`/api/credential-broker`; the stored `credential-definition/v1` record contains
|
|
only metadata and a canonical digest.
|
|
|
|
Internal consumers can issue an opaque `credential-lease/v1` handle only when
|
|
the referenced definition appears in the active attempt's immutable launch
|
|
manifest. The lease is bound to that task, attempt, manifest digest, definition
|
|
digest, scope digest, and exact action fingerprint. Handles are persisted only
|
|
as SHA-256 hashes. Uses are claimed atomically before source resolution and
|
|
enforce TTL, maximum uses, approval posture, and current run binding.
|
|
|
|
The resolved value is passed only to a controlled in-process callback. The
|
|
broker rejects callbacks that return the value and replaces callback/source
|
|
errors with credential-free failures. Definitions, leases, audit events, logs,
|
|
manifests, completion packets, and API responses never contain the resolved
|
|
value. Terminal run paths revoke matching leases, and startup reconciliation
|
|
expires stale leases or blocks leases whose source is unavailable.
|
|
|
|
This is a foundation, not a claim that provider traffic is controlled.
|
|
Required brokered presets fail closed when runtime evidence is advisory,
|
|
external, missing, or bypassable. Provider handle migration requires the
|
|
controlled network or tool boundaries documented in
|
|
[Credential Broker](CREDENTIAL-BROKER.md). Raw `env-passthrough` remains an
|
|
explicit compatibility mode and is not brokered.
|
|
|
|
Agent budget policies are enforced through the same governance path. Workspace,
|
|
agent, workflow, workflow-agent, and per-run budgets can cap tokens,
|
|
provider-reported cost, tool-call counts, runtime, retry count, and workflow
|
|
fan-out. Soft thresholds write `budget-policy` warning traces. Hard thresholds
|
|
pause or block for approval, downgrade to a configured model route, or cancel
|
|
the run with recorded trace and completion-packet evidence.
|
|
|
|
For untrusted or externally sourced work, prefer a required preset with
|
|
repository-scoped writes, default-deny network egress, metadata endpoint
|
|
blocking, and brokered credentials. Keep the legacy permissive preset only for
|
|
existing local Codex CLI workflows that still need broad compatibility.
|
|
|
|
## Configuration Reference
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
| ------------------------------- | ------- | ------------------------------------------------------- |
|
|
| `VERITAS_AUTH_ENABLED` | `true` | Enable/disable authentication |
|
|
| `VERITAS_AUTH_LOCALHOST_BYPASS` | `false` | Allow unauthenticated localhost requests in development |
|
|
| `VERITAS_ADMIN_KEY` | (none) | Admin API key with full access |
|
|
| `VERITAS_API_KEYS` | (none) | Comma-separated API keys (format: `name:key:role`) |
|
|
|
|
### API Key Format
|
|
|
|
```
|
|
name:key:role,name2:key2:role2
|
|
```
|
|
|
|
Example:
|
|
|
|
```
|
|
veritas:vk_abc123xyz:agent,dashboard:vk_def456uvw:read-only
|
|
```
|
|
|
|
## Generating API Keys
|
|
|
|
### Using OpenSSL
|
|
|
|
```bash
|
|
# Generate a random 32-character key
|
|
openssl rand -base64 32
|
|
```
|
|
|
|
### Using the Built-in Function
|
|
|
|
```typescript
|
|
import { generateApiKey } from './middleware/auth.js';
|
|
const key = generateApiKey('vk'); // e.g., vk_AbCdEf123...
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Auth Status (Unauthenticated)
|
|
|
|
Check the current authentication configuration:
|
|
|
|
```bash
|
|
curl http://localhost:3001/api/auth/status
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"enabled": true,
|
|
"localhostBypass": false,
|
|
"configuredKeys": 2,
|
|
"hasAdminKey": true
|
|
}
|
|
```
|
|
|
|
### Health Check (Unauthenticated)
|
|
|
|
```bash
|
|
curl http://localhost:3001/health
|
|
```
|
|
|
|
## WebSocket Authentication
|
|
|
|
WebSocket connections are authenticated on connect:
|
|
|
|
```javascript
|
|
// With API key
|
|
const ws = new WebSocket('ws://localhost:3001/ws?api_key=your-key');
|
|
|
|
ws.onclose = (event) => {
|
|
if (event.code === 4001) {
|
|
console.error('Authentication failed:', event.reason);
|
|
}
|
|
};
|
|
```
|
|
|
|
### WebSocket Close Codes
|
|
|
|
| Code | Meaning |
|
|
| ------ | ------------------------------ |
|
|
| `1000` | Normal close |
|
|
| `4001` | Authentication required/failed |
|
|
|
|
## Error Responses
|
|
|
|
### 401 Unauthorized
|
|
|
|
```json
|
|
{
|
|
"error": "Authentication required",
|
|
"code": "AUTH_REQUIRED",
|
|
"hint": "Provide API key via Authorization header (Bearer <key>), X-API-Key header, or api_key query parameter"
|
|
}
|
|
```
|
|
|
|
### 403 Forbidden
|
|
|
|
```json
|
|
{
|
|
"error": "Write access denied",
|
|
"code": "WRITE_FORBIDDEN",
|
|
"hint": "Your API key has read-only access"
|
|
}
|
|
```
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Never commit API keys** - Use environment variables or `.env` files (add to `.gitignore`)
|
|
|
|
2. **Rotate keys regularly** - Update API keys periodically, especially if compromised
|
|
|
|
3. **Use HTTPS in production** - API keys are transmitted in headers/URLs
|
|
|
|
4. **Principle of least privilege** - Use `read-only` for dashboards, `agent` for automation
|
|
|
|
5. **Monitor access** - The server logs connection attempts with role information
|
|
|
|
6. **Constrain agent launches** - Assign sandbox policy presets and run budgets
|
|
before running untrusted or expensive work. Default-deny network egress,
|
|
broker credentials, and cap token, spend, tool-call, runtime, retry, and
|
|
fan-out exposure when a workflow does not need broad access.
|
|
|
|
7. **Keep remote mode explicit** - Binding outside loopback, reverse proxying,
|
|
tunneling, or serving mobile/PWA clients requires auth enabled, localhost
|
|
bypass disabled, exact CORS/WebSocket origins, and redacted diagnostics. See
|
|
[ADR 0002](architecture/ADR-0002-v5-remote-server-security-posture.md).
|
|
|
|
## Migrating from No Auth
|
|
|
|
If you're upgrading from an earlier version without authentication:
|
|
|
|
1. **Before upgrading**: Document all clients that access the API
|
|
|
|
2. **During upgrade**:
|
|
- Start with `VERITAS_AUTH_LOCALHOST_BYPASS=true` for smooth transition
|
|
- Generate API keys for each client
|
|
- Update clients to include authentication headers
|
|
|
|
3. **After testing**: Disable localhost bypass for production
|
|
|
|
## Troubleshooting
|
|
|
|
### "Authentication required" for localhost
|
|
|
|
Check that `VERITAS_AUTH_LOCALHOST_BYPASS=true` is set, or provide an API key.
|
|
|
|
### "Invalid API key"
|
|
|
|
- Verify the key matches exactly (no extra spaces)
|
|
- Check that the key is in the `VERITAS_API_KEYS` or `VERITAS_ADMIN_KEY` variable
|
|
- Ensure the format is correct: `name:key:role`
|
|
|
|
### WebSocket immediately closes
|
|
|
|
- Check browser console for the close reason
|
|
- Ensure the API key is passed as a query parameter: `?api_key=...`
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Request Flow │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Client Request │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────┐ │
|
|
│ │ CORS/JSON │ (express middleware) │
|
|
│ └──────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────┐ ┌───────────────────────┐ │
|
|
│ │ /health │──▶│ Bypass auth │ │
|
|
│ │ /api/auth/* │ │ (unauthenticated) │ │
|
|
│ └──────────────┘ └───────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────┐ │
|
|
│ │ authenticate │ (middleware/auth.ts) │
|
|
│ │ │ │
|
|
│ │ - Check auth │ │
|
|
│ │ enabled │ │
|
|
│ │ - Localhost │ │
|
|
│ │ bypass? │ │
|
|
│ │ - Validate │ │
|
|
│ │ API key │ │
|
|
│ └──────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────┐ │
|
|
│ │ Route Handler│ (req.auth available) │
|
|
│ └──────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Changelog
|
|
|
|
- **v3.3.0** (2026-02-15): Task intelligence security hardening
|
|
- Crash-recovery checkpointing with auto-sanitization of 20+ secret patterns plus regex value detection
|
|
- XSS prevention in observational memory via `sanitizeCommentText()`
|
|
- DFS cycle detection in task dependencies prevents infinite loop attacks
|
|
- Input sanitization on agent filter (trim + 100 char cap)
|
|
- Zod validation on all dependency and checkpoint routes
|
|
- **v3.0.0** (2026-02-09): Workflow engine security
|
|
- ReDoS protection on regex acceptance criteria
|
|
- Expression injection prevention in template evaluator
|
|
- Parallel DoS limits (max 50 concurrent sub-steps)
|
|
- Gate approval authentication and permission checks
|
|
- RBAC with ACL files for workflow access control
|
|
- Audit logging of all workflow changes
|
|
- **v2.0.0** (2026-02-06): Multi-agent security
|
|
- Agent permission levels (Intern/Specialist/Lead) with enforcement
|
|
- Agent registry with heartbeat-based liveness tracking
|
|
- MCP SDK patched to ^1.26.0 (GHSA-345p-7cg4-v4c7)
|
|
- Rate limiting documentation (reverse proxy recommended for public deployments)
|
|
- **v1.0.0** (2026-01-29): Initial authentication implementation
|
|
- API key authentication for HTTP and WebSocket
|
|
- Role-based authorization (admin, agent, read-only)
|
|
- Localhost bypass for development
|
|
- Configuration via environment variables
|