Skip to content

Changelog

All notable changes to Shoreguard are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.26.1] — 2026-04-10

Changed

  • Docstring coverage — pydoclint clean across shoreguard/ — Every public function, method, and Pydantic model in shoreguard/ now has a Google-style docstring with Args/Returns/Raises/Attributes sections as appropriate. 410 pre-existing pydoclint violations across 21 files were fixed (96 in api/schemas.py, 64 in services/operations.py, 51 in api/pages.py, …). Zero runtime behaviour changes; this unblocks pydoclint in CI so future docstring drift gets caught at review time.
  • Removed stale linter suppressions — Systematic audit of all # noqa / # type: ignore / # pyright: ignore comments. ~150 justified suppressions kept (stdlib API signatures, SQLAlchemy column semantics, protobuf stub typing, fake gRPC test doubles, singleton PLW0603, __init__ D107, …) — each now carries a comment explaining why. 12 non-justified suppressions removed by adding proper types or narrowing: SQLAlchemy event-handler params in db.py, operation_service / gateway_service narrowing in api/main.py + api/metrics.py, _get_auth_settings / _webhook_settings / _cli_init_db return types, _UNSET sentinel cast() in services/registry.py + sandbox_meta.py. The cleanup surfaced two real type bugs: _cli_init_db was annotated -> None despite callers invoking .dispose() on the returned Engine, and the module-level operation_service carried a stale AsyncOperationService | OperationService | None union that never matched runtime reality. Both fixed; no runtime behaviour change.

[0.26.0] — 2026-04-09

Added

  • CSP strict-mode foundationSHOREGUARD_CSP_STRICT=true opt-in enables a per-request nonce on request.state.csp_nonce and an unsafe-*-free Content-Security-Policy built from auth.csp_policy_strict (default remains off until the frontend refactor lands). Templates can reference {{ csp_nonce(request) }} on inline <script> tags and switch between the standard and CSP-safe Alpine.js builds via {% if csp_strict_enabled() %}. This is Milestone 1 of the multi-session CSP hardening plan — see csp-hardening-followup.md for the full roadmap.

Changed

  • CSP hardening M2 — All inline <script> blocks extracted from Jinja templates into frontend/js/ (theme-init.js, dashboard.js, audit.js; providers.js and wizard.js bind their own DOMContentLoaded handlers). GW is now read from document.documentElement.dataset.gateway in constants.js, eliminating the last Jinja-templated inline script. With SHOREGUARD_CSP_STRICT=true, strict CSP no longer reports inline-script violations — only inline-style (M3) and Alpine x-data (M4) violations remain.
  • CSP hardening M3 — All inline style="..." attributes and <style> blocks removed from Jinja templates. Shared patterns moved to the new frontend/css/utilities.css (sg-prefixed width/max-width/font-size/cursor utilities) and auth pages share frontend/css/auth.css. Wizard step toggling now uses classList.toggle('d-none', ...) instead of element.style.display. With SHOREGUARD_CSP_STRICT=true, strict CSP no longer reports style-src violations — only Alpine x-data (M4) remains.
  • CSP hardening M4 — Every Alpine.js component is now registered via Alpine.data(name, factory) (per-file inside each frontend/js/*.js factory file, plus a new frontend/js/auth.js for the login/register/setup/invite forms). Templates reference them by name (x-data="loginForm") instead of inline object or spread-merge literals — the four { ...pageFn(), ...sortableTable(...) } patterns on the gateways/policies/users/groups pages are now gatewaysList, presetsListPage, usersListPage, groupsListPage. Directive expressions containing arrow functions, if statements, or multi-statement sequences (logout click, toast auto-remove, ws-state listener, clipboard-copy buttons, inference-config x-effect, filesystem-policy add-form focus) were extracted to store/component methods ($store.auth.logout, $store.toasts.scheduleRemove, onWsState, copyInvite, copyKey, maybeLoad, openAddForm). Auth pages now share the new components/alpine_loader.html partial with the main base template so the CSP build is loaded consistently. With SHOREGUARD_CSP_STRICT=true, the application loads with zero CSP-related Alpine violations — clearing the last blocker to making strict CSP the default in a future minor bump.
  • Pyright on tests/ + parallel test execution — Pyright's include list now covers tests/ alongside shoreguard/, and pytest-xdist is a dev dependency so the suite runs with pytest -n auto. Enabling pyright on tests surfaced 303 pre-existing errors across 19 files (Optional narrowing, fake gRPC stub assignments typed as OpenShellStub, protobuf enum kwargs passed as raw ints, and a handful of test-setup bugs such as _FakeRpcError missing cancel()). All fixed test-side — zero changes to shoreguard/ — via assert x is not None narrowing and narrow # type: ignore[assignment|arg-type|override] comments where the fake object pattern made narrowing impossible. On a 16-core box the suite now runs in ~43s parallel instead of ~4:46 serial (6.6× speedup).

[0.25.0] — 2026-04-09

Added

  • shoreguard config show [section] — dump the effective configuration as a table, JSON, or .env-style output. Secret values (secret_key, admin_password, client_secret, password) are redacted by default; --show-sensitive reveals them.
  • shoreguard config schema [section] — dump pristine defaults plus descriptions in table/json/env/markdown format. Used to regenerate docs/reference/settings.md.
  • Self-documenting settings — every Settings field now carries Field(default=..., description=...). All ~100 environment variables have a one-line description surfaced via config show.
  • shoreguard audit export — offline audit log export (JSON or CSV) with a sha256sum-compatible digest file and a manifest.json carrying entry count, filters, timestamp, and tool version. All three files are written with 0600 permissions.
  • Structured logging improvements — text mode now renders [request_id] via the RequestIdFilter (was silently dropped); JSONFormatter adds module/func/line, merges caller extras, and emits stack_info. uvicorn access logs carry the same request-id as application logs in both modes.
  • Global per-IP rate limiter (SHOREGUARD_GLOBAL_RATE_LIMIT_*) as a coarse DDoS guardrail applied by global_rate_limit_middleware to every HTTP request except health/metrics endpoints.
  • Request body size limit middleware (SHOREGUARD_LIMIT_MAX_REQUEST_BODY_BYTES, default 10 MiB) returning HTTP 413 before Starlette reads the body.
  • DB migration retry loop on startup with exponential backoff against OperationalError (SHOREGUARD_DB_STARTUP_RETRY_*). Compose-friendly.
  • Background task supervision surfaced in /readyz with asyncio.wait_for on dependency probes (SHOREGUARD_READYZ_TIMEOUT).
  • Production-readiness check expansion — six new warnings: HSTS off, CSP contains unsafe-*, allow_registration in prod, multi-replica with in-process rate limiter, SQLite in prod, text log format in prod. Warnings now carry ERROR: / WARN: severity prefixes.
  • docs/reference/settings.md — auto-generated reference of every SHOREGUARD_* environment variable grouped by sub-model.

Changed

  • Audit log is now ORM-level append-only. AuditEntry rows cannot be updated via the ORM, and deletion is only permitted from AuditService.cleanup() via a ContextVar-gated bypass. Enforcement raises AuditIntegrityError on commit. cleanup() switched to row-by-row deletion so the before_delete listener fires. Direct SQL still bypasses enforcement — DB-level triggers are a post-v1.0 item.
  • CLI callback respects ctx.invoked_subcommand — the main Typer callback no longer tries to bind a socket when shoreguard config ... or shoreguard audit ... subcommands are invoked.
  • Graceful shutdown timeout honoured by uvicorn startup path.
  • CORS settings tightened and exposed via SHOREGUARD_CORS_*.

Security

  • OIDC SSRF protectiondiscover(), get_jwks(), and exchange_code() run all URLs (including those returned by a provider's discovery document) through the existing private-IP check. A compromised identity provider can no longer pivot requests to internal services like cloud metadata endpoints.

Fixed

  • Version driftpyproject.toml was still reporting 0.23.0 after the v0.24.0 tag was cut. This release bumps directly to 0.25.0 to resync the package metadata with the release stream.

[0.24.0] — 2026-04-08

Added

  • 1,193 mutation-killing tests — targeted tests designed to eliminate survived mutants identified by mutmut v3.5. Test count: 1,175 → 2,368.
  • New test_openshell_meta.py — first-ever coverage for OpenShell metadata loader (27 mutants, previously 100% survival).
  • New test_auth_mutations.py (194 tests) — exhaustive auth CRUD, RBAC role resolution, service principal lifecycle, group management, session tokens, gateway-scoped roles.
  • Extended 20 existing test files across all major modules: formatters, sandbox templates, routes, OIDC, local gateway, webhooks, gateway service, operations, registry, policy, all client modules, DB, presets, CLI import, and audit service.

Fixed

  • Pyright strict mode — resolved all 30 type-check errors (0 remaining):
  • operation_service union type corrected for async/sync variants.
  • _get_svc() return type narrowed to AsyncOperationService in route handlers (routes/operations.py, lro.py).
  • db_cfg possibly-unbound variable in db.py PostgreSQL branch.
  • discover() return type in api/oidc.py.
  • update_group sentinel parameter type in api/auth.py.
  • Async/sync union narrowing in main.py, metrics.py, routes/gateway.py, routes/sandboxes.py.

[0.23.0] — 2026-04-08

Added

  • OIDC/SSO authentication — multi-provider support with callback flow, role mapping, and state validation (api/oidc.py, alembic/versions/012_oidc_fields.py).
  • SSRF validation — URL allowlist/blocklist for webhook targets prevents server-side request forgery via internal addresses.
  • Input sanitization — centralized validators for names, URLs, certs, env vars, and command strings with configurable limits via SHOREGUARD_LIMIT_* env vars.
  • pip-audit in CI — automated dependency vulnerability scanning in the GitHub Actions workflow.
  • Deep health checks/readyz now measures DB latency, reports gateway health summary (total/connected/degraded), supports ?verbose=true for per-gateway details.
  • PostgreSQL connection poolingDatabaseSettings with pool_size, max_overflow, pool_recycle, statement_timeout_ms via SHOREGUARD_DB_* env vars.
  • Graceful shutdown — LRO task cancellation (shutdown_lros()), webhook delivery task tracking with shutdown(), ordered resource disposal.
  • Async engine disposaldispose_async_engine() for clean DB shutdown.
  • Docs — OIDC guide, security concepts, troubleshooting, audit guide, webhooks guide, Prometheus integration, gateway roles admin.
  • 108+ new tests — OIDC, input validation, SSRF, webhook secret leak. Total: ~1194.

Changed

  • Typed API response modelsextra="forbid" on Category-A models prevents uncontrolled field leakage through extra="allow".
  • Webhook HMAC secret no longer exposed on GET/LIST endpoints — only returned on create (WebhookCreateResponse).
  • Docs restructuredguide/guides/, new concepts/ and integrations/ directories.
  • graceful_shutdown_timeout default raised from 5 → 15 seconds.

Security

  • Fixed webhook HMAC signing secret leak on all GET/PUT responses.
  • SSRF protection for webhook target URLs.
  • Input length/format validation on all mutation endpoints.

[0.22.0] — 2026-04-08

Added

  • User groups / teams — named collections of users for group-based RBAC. Groups have a global role and optional per-gateway role overrides, mirroring the existing individual user role system.
  • Group membership management — add/remove users to groups via API and frontend UI (/groups page with member modal).
  • Group gateway-scoped roles — per-gateway role overrides for groups, reusing the gateway roles modal from user/SP management.
  • 4-tier role resolution — individual gateway > group gateway > individual global > group global. When a user belongs to multiple groups the highest rank wins.
  • Group audit trailgroup.create, group.update, group.delete, group.member.add, group.member.remove, group.gateway_role.set, group.gateway_role.remove actions logged.
  • 65 new tests — CRUD, membership, cascade deletes, role resolution priority chain, and HTTP-level endpoint tests (test_group_rbac.py). Total: 1086.

Changed

  • Gateway roles modal — now supports user, sp, and group entity types.

[0.21.0] — 2026-04-07

Added

  • Rate limiting — per-IP sliding-window rate limiter (api/ratelimit.py) with configurable limits via SHOREGUARD_RATELIMIT_* env vars.
  • Account lockout — progressive lockout after failed login attempts (api/auth.py) with configurable thresholds.
  • Security headersX-Content-Type-Options, X-Frame-Options, Strict-Transport-Security, etc. via middleware (api/security_headers.py).
  • Password strength validationapi/password.py with length, complexity, and common-password checks.
  • Structured error codes — machine-readable code field (e.g. GATEWAY_NOT_FOUND, RATE_LIMITED) in all error responses (api/error_codes.py, api/errors.py).
  • WebSocket server heartbeat — periodic {"type": "heartbeat"} messages during idle with dropped_events counter for backpressure visibility.
  • WebSocket backpressure disconnect — slow consumers disconnected after configurable consecutive drop limit (SHOREGUARD_WS_BACKPRESSURE_DROP_LIMIT).
  • WebSocket client reconnect hardening — heartbeat watchdog (45 s timeout), max retry limit (20), exponential backoff, and sg:ws-state events for connection state UI indicator.
  • Prometheus metrics/metrics endpoint with login and rate-limit counters.

Changed

  • Dynamic __version__shoreguard/__init__.py now reads version from package metadata (importlib.metadata) instead of hardcoded string; single source of truth in pyproject.toml.
  • Deploy configs — consolidated Caddyfile and standalone compose into deploy/ directory.
  • .gitignore — trimmed from ~200 to ~30 lines, removed stale entries.

[0.20.0] — 2026-04-07

Added

  • Pydantic Settings — centralized shoreguard/settings.py with 11 nested sub-models replacing 11 os.environ.get() reads and 60+ hardcoded constants. All tuneable via SHOREGUARD_* env vars (e.g. SHOREGUARD_GATEWAY_BACKOFF_MIN, SHOREGUARD_OPS_RUNNING_TTL).
  • Pydantic response models — typed response schemas (schemas.py) on all API endpoints with OpenAPI tag metadata.
  • Request-ID trackingX-Request-ID header propagated through middleware, available in all log records via %(request_id)s.
  • Prometheus latency metricsshoreguard_request_duration_seconds histogram with method/path/status labels, plus /metrics endpoint.
  • Structured JSON loggingSHOREGUARD_LOG_FORMAT=json for machine-readable log output.
  • GZip compression — responses ≥ 1 KB automatically compressed via Starlette GZip middleware.
  • Audit paginationGET /api/audit supports offset/limit with items/total response format.
  • Input validation moduleapi/validation.py with reusable description, label, and gateway-name validators.
  • DB-backed operationsAsyncOperationService with SQLAlchemy async, orphan recovery, and configurable retention.
  • SSE streaming for LROsGET /api/operations/{id}/stream streams real-time status/progress updates via Server-Sent Events.
  • run_lro helperapi/lro.py with idempotency-key support, automatic 202 response, and background task lifecycle.
  • Async DB layerinit_async_db() / get_async_session_factory() in db.py for aiosqlite-backed async sessions.
  • Performance indexes — migrations 008–010 adding indexes on audit timestamp, webhook delivery, and operation status.
  • Gateway register page/gateways/new with breadcrumb navigation, description and labels fields (replaces modal).
  • Provider create/edit pages/gateways/{gw}/providers/new and .../providers/{name}/edit with Alpine.js providerForm() component (replaces modal).

Changed

  • Consistent pagination — all list endpoints return {"items": [...], "total": N} format.
  • CLI env-var hack removedcli.py no longer writes os.environ["SHOREGUARD_*"]; uses override_settings() instead.
  • Frontend modals→pages — gateway registration and provider create/edit modals replaced with dedicated page routes and breadcrumb navigation.

Removed

  • In-memory LRO store — replaced by DB-backed AsyncOperationService.
  • Hardcoded constants_BACKOFF_MIN, _MAX_RESULT_BYTES, DELIVERY_TIMEOUT, MAX_DESCRIPTION_LEN, etc. now read from Settings.
  • Gateway/provider modals#registerGatewayModal and #createProviderModal removed from frontend templates.

Dependencies

  • Added pydantic-settings>=2.0.

[0.19.0] — 2026-04-07

Added

  • Async sandbox execPOST /sandboxes/{name}/exec now returns a long-running operation (LRO) with polling pattern instead of blocking.
  • Exec audit fieldscommand, exit_code, and status added to sandbox.exec audit detail for full traceability.
  • mTLS auto-generationopenshell-client-tls secret with CA cert is automatically created for OpenShell gateway connections.
  • Docker Compose profiles — optional paperclip profile for Paperclip integration alongside ShoreGuard.
  • Caddy reverse proxy — new Caddy service and OpenClaw profile in the deploy stack for production-ready TLS termination.
  • Hardened OpenClaw sandbox — dedicated sandbox image with security documentation and deployment via generic ShoreGuard APIs.
  • Deploy stack README — ecosystem section and deploy stack overview added to the project README.

Fixed

  • gRPC exec timeout — default timeout raised to 600 s for long-running agent sessions.
  • SetClusterInferenceno_verify flag now correctly set in the gRPC request.
  • LOCAL_MODE endpoints — private IP addresses are now accepted when registering gateways in local mode.
  • Gateway context — switched from ContextVar to request.state to avoid cross-request leaks.
  • openshell-client-tls — secret now includes the CA certificate for proper chain verification.
  • sandbox_meta_store import — resolved binding issue that caused startup failures.
  • Exec tests — aligned with async LRO pattern and added shlex validation before returning 202.

Changed

  • README — redesigned with updated architecture diagram and sandbox vision narrative.
  • Architecture diagram — added multi-gateway topology, observability components, unified operators, agent platform UIs, and plugins.
  • Mermaid diagrams — improved contrast for dark-mode rendering.

Docs

  • Deploy guide expanded with profiles and Paperclip integration steps.
  • Plugin install command updated to @shoreguard/paperclip-plugin from npm.
  • Discord reference removed from OpenClaw README.

[0.18.1] — 2026-04-06

Added

  • Sandbox metadata UI — labels and description are now visible and editable across the entire frontend:
  • Detail page: Metadata fieldset with description input, label badges (add/remove), and Save button (PATCH, operator role).
  • Wizard: Description and labels fields in Step 2 (Configuration), shown in Step 4 summary, included in create payload.
  • List page: Description column (truncated) and label badges inline under sandbox name.

[0.18.0] — 2026-04-05

Added

  • Sandbox labels & description — sandboxes now support labels (key-value pairs) and description metadata, stored in ShoreGuard's DB (OpenShell is unaware). New sandbox_meta table with per-gateway scoping.
  • PATCH /sandboxes/{name} — update labels and/or description on existing sandboxes (requires operator role).
  • Label filteringGET /sandboxes?label=key:value filters sandboxes by labels (AND-combined, same semantics as gateway list).
  • Alembic migration 007 — creates sandbox_meta table with (gateway_name, sandbox_name) unique constraint.

[0.17.0] — 2026-04-05

Fixed

  • Exception handling — narrowed overly broad except Exception blocks in health check logging, webhook delivery, reconnection loop, and operation lifecycle. All handlers now log with full traceback and re-raise or return safe error responses.
  • SP expiry timezoneexpires_at comparison in _lookup_sp_identity now correctly handles naive datetimes by normalising to UTC before comparison.
  • Bootstrap adminbootstrap_admin_user() no longer raises on duplicate email when called during startup with an existing database.

Changed

  • Logging consistency — webhook delivery success/failure, gateway reconnection attempts, and operation lifecycle transitions now log at appropriate levels (INFO for business events, WARNING for recoverable errors, DEBUG for technical details).
  • Docstrings — all public functions and classes pass pydoclint with strict Google-style checking (raises, return types, class attributes).
  • Type hintsrequire_role return type corrected. Zero pyright errors on standard mode.
  • CI — Python 3.14 target for CI matrix, ruff, and pyright. Bumped docker/setup-buildx-action to v4, docker/build-push-action to v7, astral-sh/setup-uv to v7.

Added (tests only)

  • Webhook route tests — 24 integration tests covering CRUD, validation, role enforcement (admin/viewer/unauthenticated), and service-not-initialised.
  • Error-case tests — 13 tests across approvals (4), policies (3), providers (4), and sandboxes (2) for 404/409 error paths.
  • Template tests — 9 tests for sandbox_templates.py (list, get, path traversal protection) and template route handlers.
  • Webhook delivery tests — 13 tests for delivery records, cleanup, email channel dispatch, and the fire_webhook convenience function.
  • Auth endpoint tests — 31 tests for pages.py covering setup wizard, login validation, user CRUD, gateway role management, self-registration, and service principal management error paths.
  • Total: 915 tests (+86 from 0.16.2), coverage 82% → 84%.

[0.16.0] — 2026-04-04

Added

  • Webhook delivery log — new webhook_deliveries table tracks every delivery attempt with status, response code, error message, and timestamps. Query via GET /api/webhooks/{id}/deliveries.
  • Webhook retry with exponential backoff — HTTP 5xx and network errors trigger up to 3 retries (5s → 30s → 120s). Client 4xx errors fail immediately.
  • New webhook eventsgateway.registered, gateway.unregistered, inference.updated, policy.updated fire automatically after the corresponding API actions.
  • Enriched sandbox.created payload — now includes image, gpu, and providers fields from the creation request.
  • API-key rotationPOST /api/auth/service-principals/{id}/rotate generates a new key and immediately invalidates the old one (admin only).
  • API-key expiry — optional expires_at timestamp on service principals. Expired keys are rejected at auth time.
  • API-key prefix — new keys are prefixed with sg_ and the first 12 characters are stored as key_prefix for identification without exposing the full key. Legacy keys remain functional.
  • Sandbox templates — YAML-based full-stack templates (data-science, web-dev, secure-coding) that pre-configure image, GPU, providers, environment variables, and policy presets. Available via GET /api/sandbox-templates and integrated into the wizard.
  • Alembic migration 005 — adds webhook_deliveries table.
  • Alembic migration 006 — adds key_prefix and expires_at columns to service_principals table.

Changed

  • Webhook servicefire() now creates delivery records per target before dispatching. _deliver_http replaced by _deliver_http_with_retry with retry logic.
  • Service principal creation — keys now use sg_ prefix format. list_service_principals() returns key_prefix and expires_at fields.
  • Users UI — SP table shows key prefix, expiry badge (green/yellow/red), and rotate button. SP creation form includes optional expiry date.
  • Wizard UI — step 1 shows sandbox template cards above community sandboxes. Selecting a template pre-fills all fields and jumps to summary. "Customize" button navigates back to configuration step.
  • Formatters_EVENT_LABELS, _SLACK_COLORS, _DISCORD_COLORS extended for 4 new events. _payload_fields() extracts provider, model, image, and endpoint fields.
  • Cleanup loop — webhook delivery records older than 7 days are purged alongside operations and audit entries.
  • Documentation — API reference updated with sandbox templates, delivery log, rotate endpoint, and new event types. Service principals guide expanded with key rotation, expiry, and prefix sections. Sandbox guide includes templates section with wizard integration.

[0.15.0] — 2026-04-04

Added

  • Gateway description — free-text description field on gateways for documenting purpose and context (e.g. "Production EU-West for ML team").
  • Gateway labels — key-value labels (env=prod, team=ml, region=eu-west) stored as labels_json column. Kubernetes-style key validation, max 20 labels per gateway, values up to 253 chars.
  • PATCH /api/gateway/{name} — new endpoint to update gateway description and/or labels after registration (admin only). Supports partial updates via Pydantic model_fields_set.
  • Label filteringGET /api/gateway/list?label=env:prod&label=team:ml filters gateways by labels (AND semantics).
  • Alembic migration 004 — adds description (Text) and labels_json (Text) columns to the gateways table.

Changed

  • Gateway list UI — new description column (hidden on small screens) and label badges displayed below gateway names.
  • Gateway detail UI — description and labels shown in details card with inline edit form (admin only).
  • Gateway registration modal — new description textarea and labels textarea (one key=value per line).
  • GatewayRegistryregister(), _to_dict(), and list_all() extended for description, labels, and label filtering. New update_gateway_metadata() method with sentinel-based partial updates.

[0.14.0] — 2026-04-04

Added

  • Notification channels — webhooks now support channel_type field with values generic (default, HMAC-signed), slack (Block Kit formatting), discord (embed formatting), and email (SMTP delivery). Alembic migration 003 adds channel_type and extra_config columns to the webhooks table.
  • Message formattersshoreguard/services/formatters.py with channel-specific formatting: Slack Block Kit with mrkdwn and color coding, Discord embeds with color-coded fields, plain-text email bodies.
  • Prometheus /metrics endpoint — unauthenticated, exposes shoreguard_info, shoreguard_gateways_total (by status), shoreguard_operations_total (by status), shoreguard_webhook_deliveries_total (success/failed), and shoreguard_http_requests_total (by method and status code).
  • HTTP request counting middleware — counts all API requests by method and status code for Prometheus.
  • OperationStore.status_counts() — thread-safe method returning operation counts grouped by status.

Changed

  • WebhookService refactored for channel-type-aware delivery: _deliver dispatches to _deliver_http (generic/slack/discord) or _deliver_email. HMAC signature only applied for generic channel type.
  • Webhook API routes accept channel_type and extra_config in create and update requests. Email channel requires smtp_host and to_addrs in extra_config.
  • Webhook API docs expanded — channel types table, email extra_config example, corrected event types, Prometheus metrics table with scrape config.
  • Deployment docs — new monitoring section with Prometheus scrape config.
  • README — notifications and Prometheus metrics in features list and roadmap.
  • Version bumped to 0.14.0.
  • 791 tests (up from 770).

Fixed

  • deps.py type safetyget_client(), set_client(), and reset_backoff() now raise HTTPException(500) when called without a gateway context instead of passing None to the gateway service. Fixes 3 pre-existing pyright reportArgumentType errors.

Dependencies

  • Added prometheus_client>=0.21.
  • Added aiosmtplib>=3.0.

[0.13.0] — 2026-04-04

Added

  • Docker deployment polish — OCI image labels in Dockerfile, restart policies, dedicated shoreguard-net bridge network, configurable port and log level, and resource limits in docker-compose.yml.
  • .env.example — documented all environment variables with required/optional separation for quick Docker Compose setup.
  • docker-compose.dev.yml — standalone development compose with SQLite, hot-reload, no-auth, and local gateway mode. No PostgreSQL required.
  • Justfile — task runner with dev, test, lint, format, check, docker-build, docker-up, docker-down, docs, and sync targets.
  • Webhooks — event subscriptions with HMAC-SHA256 signing, Alembic migration 002, WebhookService with async delivery, and admin API (POST/GET/DELETE /api/webhooks).

Changed

  • README overhaul — new "Why ShoreGuard?" section, dual quick-start paths (pip + Docker Compose), collapsible screenshot gallery, expanded development section with Justfile references, updated roadmap.
  • Deployment docs expanded — step-by-step Docker setup, full environment variable reference table, backup/restore procedures, network isolation explanation, upgrade process, and troubleshooting section.
  • Contributing docs expanded — "Clone to first sandbox" walkthrough, Justfile task runner section, corrected clone URL and port references.
  • Local mode docs expanded — developer workflow section with --no-auth combination, SQLite defaults, and state reset instructions.
  • mkdocs nav — added migration runbook to admin guide navigation.
  • Version bumped to 0.13.0.

Fixed

  • Duplicate auth log — removed redundant "Authentication DISABLED" warning from init_auth() that appeared unformatted when running with --reload.
  • Logger name formatting — replaced one-shot name rewriting with a custom Formatter that strips the shoreguard. prefix at render time, so late-created loggers (e.g. shoreguard.db) are also shortened correctly.
  • Contributing docs — corrected clone URL (your-orgFloHofstetter) and port reference (80008888).

[0.12.0] — 2026-04-03

Added

  • Inference timeouttimeout_secs field on PUT /api/gateways/{gw}/inference allows configuring per-route request timeouts (0 = default 60s). Displayed in the gateway detail inference card.
  • L7 query parameter matchers — network policy rules can now match on URL query parameters using glob (single pattern) or any (list of patterns) matchers.

Changed

  • Protobuf stubs regenerated from OpenShell v0.0.22 (was ~v0.0.16).

[0.11.0] — 2026-04-03

Added

  • Docker containerisation — multi-stage Dockerfile and docker-compose.yml (ShoreGuard + PostgreSQL) for production deployments.
  • Health probes — unauthenticated GET /healthz (liveness) and GET /readyz (readiness — checks database and gateway service).
  • protobuf runtime dependency — added to pyproject.toml (was previously only available transitively via grpcio-tools in dev).
  • .dockerignore for minimal build context.

Fixed

  • PostgreSQL migrationusers.is_active column used server_default=sa.text("1") which fails on PostgreSQL. Changed to sa.true() for cross-database compatibility.
  • Gateway health endpointGET /api/gateways/{gw}/health called get_client() directly instead of via dependency injection, causing GatewayNotConnectedError to return 200 instead of 503.

Changed

  • FastAPI version field now matches the package version (was stale at 0.8.0).

[0.10.0] — 2026-04-03

Removed

  • "Active gateway" concept — the server-side active_gateway file (~/.config/openshell/active_gateway) is no longer read or written by the web service. Every gateway operation now requires an explicit gateway name from the URL. Removed endpoints: POST /{name}/select, GET /info, POST /start, POST /stop, POST /restart (non-named variants). The named variants (/{name}/start etc.) remain unchanged.
  • active field removed from all gateway API responses (list, info, register).
  • Service methods removed: get_active_name(), write_active_gateway(), select(), health().
  • Auto-select of first registered gateway removed from register().

Changed

  • Stateless gateway routing — the name parameter is now required on get_client(), set_client(), reset_backoff(), get_info(), and get_config(). No method falls back to the active gateway file anymore.
  • GET /infoGET /{name}/info — gateway info endpoint is now name-scoped.
  • GET /configGET /{name}/config — gateway config endpoint is now name-scoped.
  • LocalGatewayManagerstart(), stop(), restart() now require a gateway name. Connection and client management simplified: always operates on the explicitly named gateway.
  • Frontend inference config — now shows when gateway is connected (gw.connected) instead of when it was the "active" gateway (gw.active). Gateway list highlights connected gateways.
  • Health store — uses GW directly for gateway name instead of fetching from /api/gateway/info.
  • Version bumped to 0.10.0.
  • 756 tests (down from 774 — 18 tests for removed active-gateway functionality deleted).

[0.9.0] — 2026-04-03

Added

  • Sidebar navigation — collapsible sidebar with grouped navigation (Gateways, Policies, gateway-scoped Sandboxes/Providers, admin-only Audit/Users). Replaces the icon buttons in the topbar. Responsive: collapses to hamburger menu on mobile (<768px).
  • Light/dark theme toggle — switchable via sidebar button, persisted in localStorage. All custom CSS variables scoped to [data-bs-theme]; Bootstrap 5.3 handles the rest automatically.

Fixed

  • Audit page breadcrumbs — audit.html now has breadcrumbs and uses the standard layout instead of container-fluid.
  • Dashboard breadcrumbs — dashboard.html now has breadcrumbs.
  • Theme-aware tables — removed hardcoded table-dark class from all templates and JS files; tables now adapt to the active theme.

[0.8.0] — 2026-04-03

Fixed

  • RBAC response_model crash — added response_model=None to 17 route decorators (16 in pages.py, 1 in main.py) returning TemplateResponse, HTMLResponse, or RedirectResponse. Prevents FastAPI Pydantic serialization errors on non-JSON responses.
  • IntegrityError/ValueError split — gateway-role SET endpoints now return 409 on constraint conflicts and 404 on missing user/SP/gateway, instead of a blanket 404 for both.

Added

  • Migration verification tests — 5 tests (tests/test_migrations.py) covering SQLite and PostgreSQL: fresh-DB, head revision, schema-matches-models, downgrade, and PostgreSQL fresh-DB.
  • RBAC regression & validation tests — 10 new tests (tests/test_rbac.py) for DELETE gateway-role 404s, invalid gateway name 400s, and invalid role 400s (user and SP symmetry).
  • Migration check scriptscripts/verify_migrations.sh runs all Alembic migrations against a fresh database and verifies the final revision.
  • Migration CI workflow.github/workflows/test-migrations.yml runs migration tests on SQLite and PostgreSQL for PRs touching migrations or models.
  • PR template.github/PULL_REQUEST_TEMPLATE.md with migration checklist.
  • Migration runbookdocs/admin/migration-runbook.md with backup, upgrade, and rollback procedures.
  • Warning logs on error paths — all gateway-role endpoints now log logger.warning() for invalid names, invalid roles, not-found, and conflict responses.
  • Backoff for background tasks_cleanup_operations() and _health_monitor() double their interval (up to a cap) after 10 consecutive failures and reset on success.
  • postgres pytest marker in pyproject.toml.

Security

  • Shell injection fixverify_migrations.sh passes database URL via os.environ instead of bash interpolation in a Python heredoc.

Changed

  • Migrations squashed — all 7 incremental migrations replaced by a single 001_initial_schema.py that creates the final schema directly. Existing databases must be reset (rm ~/.config/shoreguard/shoreguard.db).
  • Migration CI caches uv dependencies via enable-cache: true.

[0.7.1] — 2026-04-01

Added

  • API reference docs — mkdocstrings[python] generates reference pages from existing Google-style docstrings. New pages under docs/reference/: Client, Services, API Internals, Models, and Config & Exceptions.

[0.7.0] — 2026-04-01

Added

  • pydoclint integration — new [tool.pydoclint] section in pyproject.toml with maximum strictness (Google-style, skip-checking-short-docstrings = false, all checks enabled). Added pydoclint >= 0.8 as dev dependency.
  • Comprehensive Google-style docstrings — all 1 193 pydoclint violations resolved across the entire codebase. Every function, method, and class now has Args:, Returns:, Raises:, and Yields: sections as appropriate. Compatible with mkdocstrings for future API reference generation.
  • Page templates — dedicated HTML templates for approval edit, approval history, gateway register, gateway roles, policy revisions, and provider form pages, replacing Bootstrap modal dialogs.

Changed

  • Database schema cleanup (migration 007):
  • Timestamp columns (registered_at, last_seen, created_at, last_used, timestamp) converted from String to DateTime(timezone=True) across gateways, users, service_principals, and audit_log tables.
  • gateways table rebuilt with auto-incrementing integer primary key (id) replacing the old name-based primary key.
  • user_gateway_roles and sp_gateway_roles migrated from gateway_name (String FK) to gateway_id (Integer FK) with ON DELETE CASCADE.
  • audit_log column gateway renamed to gateway_name; new gateway_id FK added with ON DELETE SET NULL.
  • Audit service refactored — uses with session_factory() context manager instead of manual session.close() in finally blocks. Gateway ID resolution via FK lookup on write.
  • Version bumped to 0.7.0.

Fixed

  • GatewayNotConnectedError in _try_connect_from_config — exception is now caught instead of propagating as an unhandled error.
  • request.state.role not set from _require_page_auth — page auth guard now correctly stores the resolved role in request state.

[0.6.0] — 2026-03-31

Added

  • Gateway-scoped RBAC — per-gateway role overrides for users and service principals. Alembic migration 006 adds user_gateway_roles and sp_gateway_roles tables.
  • Policy diff viewer — compare two policy revisions side-by-side.
  • Hardened RBAC — async correctness improvements and additional test coverage.

[0.5.0] — 2026-03-30

Added

  • Persistent audit log — all state-changing operations (sandbox/policy/gateway CRUD, user management, approvals, provider changes) are recorded in a database table with actor, role, action, resource, gateway context, and client IP.
  • Audit APIGET /api/audit lists entries with filters (actor, action, resource type, date range). GET /api/audit/export?format=csv|json exports the full log. Both endpoints are admin-only.
  • Audit page/audit admin page with filter inputs, pagination, and CSV/JSON export buttons. Built with Alpine.js.
  • Alembic migration 005audit_log table with indexes on timestamp, actor, action, and resource type.
  • Audit cleanup — entries older than 90 days are automatically purged by the existing background cleanup task.

Fixed

  • Fail-closed auth — when the database is unavailable, requests are now denied with 503 instead of silently granting admin access.
  • Async audit loggingaudit_log() is now async and runs DB writes in a thread pool via asyncio.to_thread, preventing event-loop blocking on every state-changing request.
  • UnboundLocalError in AuditServicelog(), list(), and cleanup() no longer crash if the session factory itself raises; session is now guarded with None checks in except/finally blocks.
  • Audit actor for auth events — login, setup, register, and invite-accept now set request.state.user_id before calling audit_log(), so the audit trail records the actual user instead of "unknown".
  • Failed login auditing — failed login attempts now produce a user.login_failed audit entry, enabling brute-force detection.
  • Authorization failure auditingrequire_role() now writes an auth.forbidden audit entry when a user is denied access.
  • Audit ordering in approvals — all six approval endpoints now log the audit entry after the operation succeeds, preventing false entries on failure.
  • Conditional delete auditsandbox.delete and provider.delete only write audit/log entries when the resource was actually deleted.
  • Async background cleanup — the periodic cleanup task now uses asyncio.to_thread for DB calls instead of blocking the event loop.
  • Gateway retry button — the "Retry" button in the gateway error banner now correctly calls Alpine.store('health').check() instead of the removed checkGatewayHealth() function.

Changed

  • Frontend migrated to Alpine.js — all 20+ pages rewritten from Vanilla JS template-literal rendering (innerHTML = renderX(data)) to Alpine.js reactive directives (x-data, x-for, x-text, x-show, @click). No build step required — Alpine.js loaded via CDN.
  • Three Alpine stores replace scattered global state:
  • auth — role, email, authenticated status (replaces inline script + window.SG_ROLE)
  • toasts — notification queue (replaces showToast() DOM manipulation)
  • health — gateway connectivity monitoring (replaces checkGatewayHealth() globals)
  • XSS surface reduced — Alpine's x-text auto-escapes all dynamic content, eliminating the need for manual escapeHtml() calls in templates.
  • Render functions removedrenderGatewayTable(), renderSandboxList(), renderDashboard(), and ~50 other renderX() functions replaced by declarative Alpine templates in HTML.
  • app.js slimmed — reduced from ~340 lines to ~95 lines. Only retains apiFetch(), showConfirm(), escapeHtml(), formatTimestamp(), navigateTo(), and URL helpers.
  • WebSocket integration — sandbox detail, logs, and approvals pages receive live updates via CustomEvent dispatching from websocket.js to Alpine components.
  • Version bumped to 0.5.0.
  • 717 tests (up from 710), including audit service, API route, and DB schema tests.

[0.4.0] — 2026-03-30

Added

  • User-based RBAC — three-tier role hierarchy (admin → operator → viewer) replaces the single shared API key. Users authenticate with email + password via session cookies; service principals use Bearer tokens for API/CI access.
  • Invite flow — admins invite users by email. The invite generates a single-use, time-limited token (7 days). The invitee sets their password on the /invite page and receives a session cookie.
  • Self-registration — opt-in via SHOREGUARD_ALLOW_REGISTRATION=1. New users register as viewers. Disabled by default.
  • Setup wizard — first-run /setup page creates the initial admin account. All API access is blocked until setup is complete.
  • Service principals — named API keys with roles, created by admins. Keys are SHA-256 hashed (never stored in plaintext). last_used timestamp tracked on each request.
  • User management UI/users page for admins with invite form, role badges, and delete actions. Dedicated /users/new and /users/new-service-principal pages replace the old modal dialogs.
  • Error pages — styled error pages for 403, 404, and other HTTP errors instead of raw JSON responses in the browser.
  • User email in navbar — logged-in user email and role badge shown in the navigation bar.
  • Alembic migrations 002–004api_keys table, users + service_principals tables with FK constraints, invite token hashing.
  • CLI commandscreate-user, delete-user, list-users, create-service-principal, delete-service-principal, list-service-principals.
  • 710 tests (up from 635), including comprehensive RBAC, auth flow, invite expiry, self-deletion guard, and last-admin protection tests.

Changed

  • Auth module rewrittenshoreguard/api/auth.py expanded from ~100 to ~700 lines. Session tokens are HMAC-signed with a 5-part format (nonce.expiry.user_id.role.signature). Roles are always verified against the database, not the session token, so demotions take effect immediately.
  • All state-changing endpoints now enforce minimum role via require_role() FastAPI dependency (admin for user/SP management and gateway registration; operator for sandbox/policy/provider operations).
  • Frontend role-based UI — buttons and nav items hidden based on role via data-sg-min-role attributes. escapeHtml() used consistently across all JavaScript files.
  • Policies router split — preset routes (/api/policies/presets) are mounted globally; sandbox policy routes remain gateway-scoped only. Fixes a bug where /api/sandboxes/{name}/policy was reachable without gateway context.
  • Audit logging standardised — all log messages use actor= consistently. Role denials now include method, path, and actor. IntegrityError on duplicate user/SP creation is logged. Logout resolves email instead of numeric user ID.

Fixed

  • Timing attack in authenticate_user() — bcrypt verification now runs against a dummy hash when the user does not exist, preventing email enumeration via response time analysis.
  • Policies router double-inclusion — the full policies router was mounted both globally and under the gateway prefix, exposing sandbox policy routes without gateway context. Now only preset routes are global.
  • Missing exception handlingis_setup_complete(), list_users(), and list_service_principals() now catch SQLAlchemyError instead of letting database errors propagate as 500s.
  • verify_password() bare Exception catch — narrowed to (ValueError, TypeError) to avoid masking unexpected errors.
  • WebSocket XSSsandboxName in toast messages is now escaped with escapeHtml(). Log level CSS class validated against a whitelist.
  • delete_filesystem_path missing Query annotationpath parameter now uses explicit Query(...) instead of relying on FastAPI inference.
  • Migration 004 downgrade documented as non-reversible (SHA-256 hashes cannot be reversed; pending invites are invalidated on downgrade).

Security

  • Constant-time authentication prevents timing-based email enumeration.
  • Invite tokens are SHA-256 hashed in the database (migration 004).
  • Session invalidation on user deletion and deactivation — existing sessions are rejected on the next request.
  • Last-admin guard with database-level FOR UPDATE lock prevents TOCTOU race.
  • Self-deletion guard prevents admins from deleting their own account.
  • Email normalisation (.strip().lower()) prevents duplicate accounts.
  • Password length enforced (8–128 characters) on all auth endpoints.
  • XSS escaping hardened across all frontend JavaScript files.

Dependencies

  • Added pwdlib[bcrypt] — password hashing with bcrypt.

[0.3.0] — 2026-03-28

Added

  • Central gateway management — Shoreguard transforms from a local sidecar into a central management plane for multiple remote OpenShell gateways (like Rancher for Kubernetes clusters). Gateways are deployed independently and registered with Shoreguard via API.
  • SQLAlchemy ORM + Alembic — persistent gateway registry backed by SQLAlchemy with automatic embedded migrations on startup. SQLite by default, PostgreSQL via SHOREGUARD_DATABASE_URL for container deployments.
  • Gateway registration APIPOST /api/gateway/register to register remote gateways with endpoint, auth mode, and mTLS certificates. DELETE /api/gateway/{name} to unregister. POST /{name}/test-connection to explicitly test connectivity.
  • ShoreGuardClient.from_credentials() — new factory method that accepts raw certificate bytes from the database instead of filesystem paths.
  • Background health monitor — probes all registered gateways every 30 seconds and updates health status (last_seen, last_status) in the registry.
  • import-gateways CLI command — imports gateways from openshell filesystem config (~/.config/openshell/gateways/) into the database, including mTLS certificates. Replaces the old migrate-v2 command.
  • SHOREGUARD_DATABASE_URL — environment variable to configure an external database (PostgreSQL) for container/multi-instance deployments.
  • --local / SHOREGUARD_LOCAL_MODE — opt-in flag to enable local Docker container lifecycle management (start/stop/restart/create/destroy). In local mode, filesystem gateways are auto-imported into the database on startup.
  • --database-url / SHOREGUARD_DATABASE_URL — all env vars now also available as CLI flags.

Changed

  • GatewayService refactored — reduced from ~800 to ~250 lines. Gateway discovery now queries the SQLAlchemy registry instead of scanning the filesystem. Connection management (backoff, health checks) preserved.
  • Docker/CLI methods extracted to LocalGatewayManager (shoreguard/services/local_gateway.py), only active in local mode.
  • Frontend updated — "Create Gateway" replaced with "Register Gateway" modal (endpoint, auth mode, PEM certificate upload). Start/Stop/Restart buttons replaced with "Test Connection". "Destroy" renamed to "Unregister". New "Last Seen" column, Port column removed.
  • API route changesPOST /create (202 LRO) → POST /register (201 sync). POST /{name}/destroyDELETE /{name}. Local lifecycle routes (start/stop/restart/diagnostics) return 404 unless SHOREGUARD_LOCAL_MODE=1.
  • Request-level logging — gateway register, unregister, test-connection, and select routes now log at INFO/WARNING level. LocalGatewayManager logs Docker daemon errors, port conflicts, missing openshell CLI, and openshell command failures.
  • api/main.py split into modules — extracted cli.py (Typer CLI + import logic), pages.py (HTML routes + auth endpoints), websocket.py (WebSocket handler), and errors.py (exception handlers). main.py reduced from 1 084 to ~190 lines (pure wiring).
  • Version bumped to 0.3.0.
  • Test suite rewritten for registry-backed architecture (635 tests).
  • Logger names standardised — all modules now use getLogger(__name__) instead of hardcoded "shoreguard". Removes duplicate log lines caused by parent-logger propagation.
  • Unified log format — single format (HH:MM:SS LEVEL module message) shared by shoreguard and uvicorn loggers with fixed-width aligned columns.
  • Duplicate "API-key authentication enabled" log line removed.

Fixed

  • SSRF protection_is_private_ip() now performs real DNS resolution instead of AI_NUMERICHOST. Hostnames that resolve to private/loopback/ link-local addresses are correctly blocked. Includes a 2 s DNS timeout.
  • import-gateways crash on single gatewayregistry.register() failures no longer abort the entire import; individual errors are logged and skipped.
  • from_active_cluster error handling — missing metadata files, corrupt JSON, and missing gateway_endpoint keys now raise GatewayNotConnectedError with a clear message instead of raw FileNotFoundError / KeyError.
  • init_db() failure logging — database initialisation errors in the FastAPI lifespan are now logged before re-raising.
  • _get_gateway_service() guard — raises RuntimeError if called before the app lifespan has initialised the service (instead of AttributeError on None).
  • WebSocket RuntimeError swallowedRuntimeError during websocket.send_json() is now debug-logged instead of silently passed.
  • SQLite pragma errors — failures setting WAL/busy_timeout/synchronous pragmas are now logged as warnings.
  • _import_filesystem_gateways SSRF gap — filesystem-imported gateways were not checked against is_private_ip(). Now blocked in non-local mode, consistent with the API registration endpoint.
  • _import_filesystem_gateways skipped count — corrupt metadata JSON was logged but not counted in the skipped total, making the summary misleading.
  • _import_filesystem_gateways mTLS read errorread_bytes() on cert files had no error handling (TOCTOU race). Now wrapped in try/except with a 64 KB size limit matching the API route.
  • check_all_health DB error isolation — a database error updating health for one gateway no longer prevents health updates for all remaining gateways.
  • select() implicit name resolutionget_client() was called without name=, relying on a filesystem round-trip via active_gateway file. Now passes the name explicitly.
  • CLI import-gateways NameError — if init_db() failed, engine was undefined and engine.dispose() in the finally block raised NameError.
  • DB engine not disposed on shutdown — the SQLAlchemy engine was not disposed during FastAPI lifespan shutdown, skipping the SQLite WAL checkpoint.
  • Docker start/stop errors silently swallowedSubprocessError/OSError in _docker_start_container/_docker_stop_container was caught but never logged.
  • Gateway start retry without summary — when all 10 connection retries failed after a gateway start, no warning was logged.
  • Frontend 404 on gateway list pageinference-providers was fetched without a gateway context, hitting a non-existent global route.

Security

  • SSRF DNS resolution bypass fixed (hostnames resolving to RFC 1918 / loopback addresses were not blocked).
  • SSRF validation includes DNS timeout protection (2 s) to prevent slow-DNS attacks.
  • remote_host input validationCreateGatewayRequest.remote_host is now validated with a hostname regex (max 253 chars) before being passed to subprocess.
  • SSRF check skipped in local modeis_private_ip() checks at connect-time and import-time now allow private/loopback addresses when SHOREGUARD_LOCAL_MODE is set, since locally managed gateways always run on 127.0.0.1.

Dependencies

  • Added sqlalchemy >= 2.0 (runtime) — ORM and database abstraction.
  • Added alembic >= 1.15 (runtime) — embedded schema migrations on startup.

[0.2.0] — 2026-03-27

Added

  • API-key authentication — optional shared API key via --api-key flag or SHOREGUARD_API_KEY env var. Supports Bearer tokens, HMAC-signed session cookies, and WebSocket query-param auth. Zero-config local development remains unchanged (auth is a no-op when no key is set).
  • Login page for the web UI with session cookie management and automatic redirect for unauthenticated users.
  • Long-Running Operations (LRO) — gateway and sandbox creation now return 202 Accepted with an operation ID. Clients can poll /api/operations/{id} for progress. Includes automatic cleanup of expired operations.
  • force flag for gateway destroy with dependency checking — prevents accidental deletion of gateways that still have running sandboxes unless --force is passed.
  • UNIMPLEMENTED error handling — gRPC UNIMPLEMENTED errors now return a human-readable 501 response with feature context instead of a generic 500.
  • OpenAPI documentation is automatically hidden when authentication is enabled.
  • Session cookies set secure flag automatically when served over HTTPS.
  • DEADLINE_EXCEEDED mapping — gRPC DEADLINE_EXCEEDED wird jetzt auf HTTP 504 (Gateway Timeout) gemappt.
  • ValidationError exception — neuer Fehlertyp für Eingabevalidierung (ungültige Namen, shlex-Fehler) mit HTTP 400 Response.
  • Gateway/Sandbox name validation — Regex-basierte Validierung von Ressourcennamen zur Verhinderung von Argument-Injection.
  • Client-IP tracking — Client-IP wird bei Auth-Fehlern und Login-Fehlversuchen mitgeloggt.

Changed

  • Sandbox creation returns 202 Accepted (was 201 Created) to reflect the asynchronous LRO pattern.
  • Destroyed gateways are now filtered from the gateway list by default.
  • Version bumped to 0.2.0.
  • Exception-Handler im gesamten Codebase von breitem except Exception auf spezifische Typen (grpc.RpcError, OSError, ssl.SSLError, ConnectionError, TimeoutError) eingeschränkt.
  • Logging deutlich erweitert: Debug-Logging für bisher stille Pass-Blöcke, Error-Level für Status ≥ 500, Warning-Level für Status < 500.
  • WebSocket-Auth-Logging von INFO/WARNING auf DEBUG normalisiert.
  • friendly_grpc_error() prüft jetzt freundliche Nachrichten vor Raw-Details.

Fixed

  • Auth credential check logic deduplicated into a single check_request_auth() helper shared by API dependencies, the /api/auth/check endpoint, and page auth guards.
  • Fire-and-forget Task-GC — Background-Tasks werden jetzt in einem Set gehalten, um Garbage-Collection durch asyncio zu verhindern.
  • Cross-Thread WebSocket-Signalingasyncio.Event durch threading.Event ersetzt für korrekte Thread-übergreifende Signalisierung.
  • WebSocket Queue-OverflowQueueFull-Exception wird abgefangen mit Fallback auf cancel_event.
  • Event-Loop-Blockingget_client() im WebSocket-Handler mit asyncio.to_thread() gewrappt.
  • gRPC-Client-Leak — Client-Leak in _try_connect() behoben, wenn Health-Check fehlschlägt.
  • Login-Redirect-Validation — Open-Redirect-Schutz: URLs die nicht mit / beginnen oder mit // starten werden abgelehnt.
  • Error-Message-Sanitizationfriendly_grpc_error() verhindert, dass rohe gRPC-Fehlermeldungen an API-Clients geleitet werden.
  • Thread-Safetythreading.Lock für GatewayService._clients und thread-safe Reads in OperationStore.to_dict().
  • YAML-Parsing-RobustheitYAMLError, None- und Skalar-Werte werden in presets.py abgefangen.
  • Metadata-Datei-RobustheitJSONDecodeError und OSError bei Gateway-Metadata-Reads mit Fallback behandelt.

Security

  • Open-Redirect-Schutz auf der Login-Seite.
  • API-Fehlermeldungen werden sanitisiert, um interne Details nicht preiszugeben.
  • Thread-sichere Client-Verwaltung und Operation-Store-Zugriffe.
  • Argument-Injection-Prävention durch Regex-Namensvalidierung.
  • Client-IP-Logging bei Auth-Events für Security-Monitoring.

[0.1.0] — 2026-03-25

Initial release.

Added

  • Sandbox management — create, list, get, delete sandboxes with custom images, environment variables, GPU support, and provider integrations.
  • Real-time monitoring — WebSocket streaming of sandbox logs, events, and status changes.
  • Command execution — run commands inside sandboxes with stdout/stderr capture.
  • SSH sessions — create and revoke interactive SSH terminal sessions.
  • Security policy editor — visual network rule, filesystem access, and process/Landlock policy management without raw YAML editing.
  • Policy approval workflow — review, approve, reject, or edit agent- requested endpoint rules with real-time WebSocket notifications.
  • Policy presets — 9 bundled templates (PyPI, npm, Docker Hub, NVIDIA NGC, HuggingFace, Slack, Discord, Telegram, Jira, Microsoft Outlook).
  • Multi-gateway support — manage multiple OpenShell gateways with status monitoring, diagnostics, and automatic reconnection.
  • Provider management — CRUD for inference/API providers with credential templates and community sandbox browser.
  • Sandbox wizard — guided step-by-step sandbox creation with agent type selection and one-click preset application.
  • Web dashboard — responsive Bootstrap 5 UI with gateway, sandbox, policy, approval, log, and terminal views.
  • REST API — full async FastAPI backend with Swagger UI documentation.
  • CLIshoreguard command with configurable host, port, log level, and auto-reload.