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 inshoreguard/now has a Google-style docstring withArgs/Returns/Raises/Attributessections as appropriate. 410 pre-existing pydoclint violations across 21 files were fixed (96 inapi/schemas.py, 64 inservices/operations.py, 51 inapi/pages.py, …). Zero runtime behaviour changes; this unblockspydoclintin CI so future docstring drift gets caught at review time. - Removed stale linter suppressions — Systematic audit of all
# noqa/# type: ignore/# pyright: ignorecomments. ~150 justified suppressions kept (stdlib API signatures, SQLAlchemy column semantics, protobuf stub typing, fake gRPC test doubles, singletonPLW0603,__init__D107, …) — each now carries a comment explaining why. 12 non-justified suppressions removed by adding proper types or narrowing: SQLAlchemy event-handler params indb.py,operation_service/gateway_servicenarrowing inapi/main.py+api/metrics.py,_get_auth_settings/_webhook_settings/_cli_init_dbreturn types,_UNSETsentinelcast()inservices/registry.py+sandbox_meta.py. The cleanup surfaced two real type bugs:_cli_init_dbwas annotated-> Nonedespite callers invoking.dispose()on the returnedEngine, and the module-leveloperation_servicecarried a staleAsyncOperationService | OperationService | Noneunion that never matched runtime reality. Both fixed; no runtime behaviour change.
[0.26.0] — 2026-04-09¶
Added¶
- CSP strict-mode foundation —
SHOREGUARD_CSP_STRICT=trueopt-in enables a per-request nonce onrequest.state.csp_nonceand an unsafe-*-free Content-Security-Policy built fromauth.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 — seecsp-hardening-followup.mdfor the full roadmap.
Changed¶
- CSP hardening M2 — All inline
<script>blocks extracted from Jinja templates intofrontend/js/(theme-init.js,dashboard.js,audit.js;providers.jsandwizard.jsbind their ownDOMContentLoadedhandlers).GWis now read fromdocument.documentElement.dataset.gatewayinconstants.js, eliminating the last Jinja-templated inline script. WithSHOREGUARD_CSP_STRICT=true, strict CSP no longer reports inline-script violations — only inline-style (M3) and Alpinex-data(M4) violations remain. - CSP hardening M3 — All inline
style="..."attributes and<style>blocks removed from Jinja templates. Shared patterns moved to the newfrontend/css/utilities.css(sg-prefixed width/max-width/font-size/cursor utilities) and auth pages sharefrontend/css/auth.css. Wizard step toggling now usesclassList.toggle('d-none', ...)instead ofelement.style.display. WithSHOREGUARD_CSP_STRICT=true, strict CSP no longer reportsstyle-srcviolations — only Alpinex-data(M4) remains. - CSP hardening M4 — Every Alpine.js component is now registered via
Alpine.data(name, factory)(per-file inside eachfrontend/js/*.jsfactory file, plus a newfrontend/js/auth.jsfor 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 nowgatewaysList,presetsListPage,usersListPage,groupsListPage. Directive expressions containing arrow functions,ifstatements, or multi-statement sequences (logout click, toast auto-remove, ws-state listener, clipboard-copy buttons, inference-configx-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 newcomponents/alpine_loader.htmlpartial with the main base template so the CSP build is loaded consistently. WithSHOREGUARD_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 coverstests/alongsideshoreguard/, andpytest-xdistis a dev dependency so the suite runs withpytest -n auto. Enabling pyright on tests surfaced 303 pre-existing errors across 19 files (Optional narrowing, fake gRPC stub assignments typed asOpenShellStub, protobuf enum kwargs passed as raw ints, and a handful of test-setup bugs such as_FakeRpcErrormissingcancel()). All fixed test-side — zero changes toshoreguard/— viaassert x is not Nonenarrowing 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-sensitivereveals them.shoreguard config schema [section]— dump pristine defaults plus descriptions in table/json/env/markdown format. Used to regeneratedocs/reference/settings.md.- Self-documenting settings — every
Settingsfield now carriesField(default=..., description=...). All ~100 environment variables have a one-line description surfaced viaconfig show. shoreguard audit export— offline audit log export (JSON or CSV) with asha256sum-compatible digest file and amanifest.jsoncarrying 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 theRequestIdFilter(was silently dropped);JSONFormatteraddsmodule/func/line, merges caller extras, and emitsstack_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 byglobal_rate_limit_middlewareto 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
/readyzwithasyncio.wait_foron dependency probes (SHOREGUARD_READYZ_TIMEOUT). - Production-readiness check expansion — six new warnings: HSTS off,
CSP contains
unsafe-*,allow_registrationin prod, multi-replica with in-process rate limiter, SQLite in prod, text log format in prod. Warnings now carryERROR:/WARN:severity prefixes. docs/reference/settings.md— auto-generated reference of everySHOREGUARD_*environment variable grouped by sub-model.
Changed¶
- Audit log is now ORM-level append-only.
AuditEntryrows cannot be updated via the ORM, and deletion is only permitted fromAuditService.cleanup()via aContextVar-gated bypass. Enforcement raisesAuditIntegrityErroron commit.cleanup()switched to row-by-row deletion so thebefore_deletelistener 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 whenshoreguard config ...orshoreguard audit ...subcommands are invoked. - Graceful shutdown timeout honoured by uvicorn startup path.
- CORS settings tightened and exposed via
SHOREGUARD_CORS_*.
Security¶
- OIDC SSRF protection —
discover(),get_jwks(), andexchange_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 drift —
pyproject.tomlwas still reporting0.23.0after 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_serviceunion type corrected for async/sync variants._get_svc()return type narrowed toAsyncOperationServicein route handlers (routes/operations.py,lro.py).db_cfgpossibly-unbound variable indb.pyPostgreSQL branch.discover()return type inapi/oidc.py.update_groupsentinel parameter type inapi/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 —
/readyznow measures DB latency, reports gateway health summary (total/connected/degraded), supports?verbose=truefor per-gateway details. - PostgreSQL connection pooling —
DatabaseSettingswithpool_size,max_overflow,pool_recycle,statement_timeout_msviaSHOREGUARD_DB_*env vars. - Graceful shutdown — LRO task cancellation (
shutdown_lros()), webhook delivery task tracking withshutdown(), ordered resource disposal. - Async engine disposal —
dispose_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 models —
extra="forbid"on Category-A models prevents uncontrolled field leakage throughextra="allow". - Webhook HMAC secret no longer exposed on GET/LIST endpoints — only
returned on create (
WebhookCreateResponse). - Docs restructured —
guide/→guides/, newconcepts/andintegrations/directories. graceful_shutdown_timeoutdefault 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 (
/groupspage 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 trail —
group.create,group.update,group.delete,group.member.add,group.member.remove,group.gateway_role.set,group.gateway_role.removeactions 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, andgroupentity types.
[0.21.0] — 2026-04-07¶
Added¶
- Rate limiting — per-IP sliding-window rate limiter (
api/ratelimit.py) with configurable limits viaSHOREGUARD_RATELIMIT_*env vars. - Account lockout — progressive lockout after failed login attempts
(
api/auth.py) with configurable thresholds. - Security headers —
X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security, etc. via middleware (api/security_headers.py). - Password strength validation —
api/password.pywith length, complexity, and common-password checks. - Structured error codes — machine-readable
codefield (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 withdropped_eventscounter 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-stateevents for connection state UI indicator. - Prometheus metrics —
/metricsendpoint with login and rate-limit counters.
Changed¶
- Dynamic
__version__—shoreguard/__init__.pynow reads version from package metadata (importlib.metadata) instead of hardcoded string; single source of truth inpyproject.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.pywith 11 nested sub-models replacing 11os.environ.get()reads and 60+ hardcoded constants. All tuneable viaSHOREGUARD_*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 tracking —
X-Request-IDheader propagated through middleware, available in all log records via%(request_id)s. - Prometheus latency metrics —
shoreguard_request_duration_secondshistogram with method/path/status labels, plus/metricsendpoint. - Structured JSON logging —
SHOREGUARD_LOG_FORMAT=jsonfor machine-readable log output. - GZip compression — responses ≥ 1 KB automatically compressed via Starlette GZip middleware.
- Audit pagination —
GET /api/auditsupportsoffset/limitwithitems/totalresponse format. - Input validation module —
api/validation.pywith reusable description, label, and gateway-name validators. - DB-backed operations —
AsyncOperationServicewith SQLAlchemy async, orphan recovery, and configurable retention. - SSE streaming for LROs —
GET /api/operations/{id}/streamstreams real-time status/progress updates via Server-Sent Events. run_lrohelper —api/lro.pywith idempotency-key support, automatic 202 response, and background task lifecycle.- Async DB layer —
init_async_db()/get_async_session_factory()indb.pyfor aiosqlite-backed async sessions. - Performance indexes — migrations 008–010 adding indexes on audit timestamp, webhook delivery, and operation status.
- Gateway register page —
/gateways/newwith breadcrumb navigation, description and labels fields (replaces modal). - Provider create/edit pages —
/gateways/{gw}/providers/newand.../providers/{name}/editwith Alpine.jsproviderForm()component (replaces modal).
Changed¶
- Consistent pagination — all list endpoints return
{"items": [...], "total": N}format. - CLI env-var hack removed —
cli.pyno longer writesos.environ["SHOREGUARD_*"]; usesoverride_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 —
#registerGatewayModaland#createProviderModalremoved from frontend templates.
Dependencies¶
- Added
pydantic-settings>=2.0.
[0.19.0] — 2026-04-07¶
Added¶
- Async sandbox exec —
POST /sandboxes/{name}/execnow returns a long-running operation (LRO) with polling pattern instead of blocking. - Exec audit fields —
command,exit_code, andstatusadded tosandbox.execaudit detail for full traceability. - mTLS auto-generation —
openshell-client-tlssecret with CA cert is automatically created for OpenShell gateway connections. - Docker Compose profiles — optional
paperclipprofile 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.
- SetClusterInference —
no_verifyflag 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
ContextVartorequest.stateto 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-pluginfrom 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) anddescriptionmetadata, stored in ShoreGuard's DB (OpenShell is unaware). Newsandbox_metatable with per-gateway scoping. PATCH /sandboxes/{name}— update labels and/or description on existing sandboxes (requires operator role).- Label filtering —
GET /sandboxes?label=key:valuefilters sandboxes by labels (AND-combined, same semantics as gateway list). - Alembic migration 007 — creates
sandbox_metatable with(gateway_name, sandbox_name)unique constraint.
[0.17.0] — 2026-04-05¶
Fixed¶
- Exception handling — narrowed overly broad
except Exceptionblocks 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 timezone —
expires_atcomparison in_lookup_sp_identitynow correctly handles naive datetimes by normalising to UTC before comparison. - Bootstrap admin —
bootstrap_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
pydoclintwith strict Google-style checking (raises, return types, class attributes). - Type hints —
require_rolereturn type corrected. Zeropyrighterrors on standard mode. - CI — Python 3.14 target for CI matrix, ruff, and pyright.
Bumped
docker/setup-buildx-actionto v4,docker/build-push-actionto v7,astral-sh/setup-uvto 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_webhookconvenience function. - Auth endpoint tests — 31 tests for
pages.pycovering 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_deliveriestable tracks every delivery attempt with status, response code, error message, and timestamps. Query viaGET /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 events —
gateway.registered,gateway.unregistered,inference.updated,policy.updatedfire automatically after the corresponding API actions. - Enriched sandbox.created payload — now includes
image,gpu, andprovidersfields from the creation request. - API-key rotation —
POST /api/auth/service-principals/{id}/rotategenerates a new key and immediately invalidates the old one (admin only). - API-key expiry — optional
expires_attimestamp 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 askey_prefixfor 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 viaGET /api/sandbox-templatesand integrated into the wizard. - Alembic migration 005 — adds
webhook_deliveriestable. - Alembic migration 006 — adds
key_prefixandexpires_atcolumns toservice_principalstable.
Changed¶
- Webhook service —
fire()now creates delivery records per target before dispatching._deliver_httpreplaced by_deliver_http_with_retrywith retry logic. - Service principal creation — keys now use
sg_prefix format.list_service_principals()returnskey_prefixandexpires_atfields. - 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_COLORSextended 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
descriptionfield 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 aslabels_jsoncolumn. 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 Pydanticmodel_fields_set.- Label filtering —
GET /api/gateway/list?label=env:prod&label=team:mlfilters gateways by labels (AND semantics). - Alembic migration 004 — adds
description(Text) andlabels_json(Text) columns to thegatewaystable.
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=valueper line). GatewayRegistry—register(),_to_dict(), andlist_all()extended for description, labels, and label filtering. Newupdate_gateway_metadata()method with sentinel-based partial updates.
[0.14.0] — 2026-04-04¶
Added¶
- Notification channels — webhooks now support
channel_typefield with valuesgeneric(default, HMAC-signed),slack(Block Kit formatting),discord(embed formatting), andemail(SMTP delivery). Alembic migration 003 addschannel_typeandextra_configcolumns to thewebhookstable. - Message formatters —
shoreguard/services/formatters.pywith channel-specific formatting: Slack Block Kit with mrkdwn and color coding, Discord embeds with color-coded fields, plain-text email bodies. - Prometheus
/metricsendpoint — unauthenticated, exposesshoreguard_info,shoreguard_gateways_total(by status),shoreguard_operations_total(by status),shoreguard_webhook_deliveries_total(success/failed), andshoreguard_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¶
WebhookServicerefactored for channel-type-aware delivery:_deliverdispatches to_deliver_http(generic/slack/discord) or_deliver_email. HMAC signature only applied forgenericchannel type.- Webhook API routes accept
channel_typeandextra_configin create and update requests. Email channel requiressmtp_hostandto_addrsinextra_config. - Webhook API docs expanded — channel types table, email
extra_configexample, 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.pytype safety —get_client(),set_client(), andreset_backoff()now raiseHTTPException(500)when called without a gateway context instead of passingNoneto the gateway service. Fixes 3 pre-existing pyrightreportArgumentTypeerrors.
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, dedicatedshoreguard-netbridge network, configurable port and log level, and resource limits indocker-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, andsynctargets. - Webhooks — event subscriptions with HMAC-SHA256 signing, Alembic
migration 002,
WebhookServicewith 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-authcombination, 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
Formatterthat strips theshoreguard.prefix at render time, so late-created loggers (e.g.shoreguard.db) are also shortened correctly. - Contributing docs — corrected clone URL (
your-org→FloHofstetter) and port reference (8000→8888).
[0.12.0] — 2026-04-03¶
Added¶
- Inference timeout —
timeout_secsfield onPUT /api/gateways/{gw}/inferenceallows 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) orany(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
Dockerfileanddocker-compose.yml(ShoreGuard + PostgreSQL) for production deployments. - Health probes — unauthenticated
GET /healthz(liveness) andGET /readyz(readiness — checks database and gateway service). protobufruntime dependency — added topyproject.toml(was previously only available transitively viagrpcio-toolsin dev)..dockerignorefor minimal build context.
Fixed¶
- PostgreSQL migration —
users.is_activecolumn usedserver_default=sa.text("1")which fails on PostgreSQL. Changed tosa.true()for cross-database compatibility. - Gateway health endpoint —
GET /api/gateways/{gw}/healthcalledget_client()directly instead of via dependency injection, causingGatewayNotConnectedErrorto return 200 instead of 503.
Changed¶
- FastAPI
versionfield now matches the package version (was stale at0.8.0).
[0.10.0] — 2026-04-03¶
Removed¶
- "Active gateway" concept — the server-side
active_gatewayfile (~/.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}/startetc.) remain unchanged. activefield 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
nameparameter is now required onget_client(),set_client(),reset_backoff(),get_info(), andget_config(). No method falls back to the active gateway file anymore. GET /info→GET /{name}/info— gateway info endpoint is now name-scoped.GET /config→GET /{name}/config— gateway config endpoint is now name-scoped.LocalGatewayManager—start(),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
GWdirectly 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-darkclass 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=Noneto 17 route decorators (16 inpages.py, 1 inmain.py) returningTemplateResponse,HTMLResponse, orRedirectResponse. 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 script —
scripts/verify_migrations.shruns all Alembic migrations against a fresh database and verifies the final revision. - Migration CI workflow —
.github/workflows/test-migrations.ymlruns migration tests on SQLite and PostgreSQL for PRs touching migrations or models. - PR template —
.github/PULL_REQUEST_TEMPLATE.mdwith migration checklist. - Migration runbook —
docs/admin/migration-runbook.mdwith 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. postgrespytest marker inpyproject.toml.
Security¶
- Shell injection fix —
verify_migrations.shpasses database URL viaos.environinstead of bash interpolation in a Python heredoc.
Changed¶
- Migrations squashed — all 7 incremental migrations replaced by a single
001_initial_schema.pythat creates the final schema directly. Existing databases must be reset (rm ~/.config/shoreguard/shoreguard.db). - Migration CI caches
uvdependencies viaenable-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 inpyproject.tomlwith maximum strictness (Google-style,skip-checking-short-docstrings = false, all checks enabled). Addedpydoclint >= 0.8as 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:, andYields: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 fromStringtoDateTime(timezone=True)acrossgateways,users,service_principals, andaudit_logtables. gatewaystable rebuilt with auto-incrementing integer primary key (id) replacing the oldname-based primary key.user_gateway_rolesandsp_gateway_rolesmigrated fromgateway_name(String FK) togateway_id(Integer FK) withON DELETE CASCADE.audit_logcolumngatewayrenamed togateway_name; newgateway_idFK added withON DELETE SET NULL.- Audit service refactored — uses
with session_factory()context manager instead of manualsession.close()in finally blocks. Gateway ID resolution via FK lookup on write. - Version bumped to
0.7.0.
Fixed¶
GatewayNotConnectedErrorin_try_connect_from_config— exception is now caught instead of propagating as an unhandled error.request.state.rolenot 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_rolesandsp_gateway_rolestables. - 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 API —
GET /api/auditlists entries with filters (actor, action, resource type, date range).GET /api/audit/export?format=csv|jsonexports the full log. Both endpoints are admin-only. - Audit page —
/auditadmin page with filter inputs, pagination, and CSV/JSON export buttons. Built with Alpine.js. - Alembic migration 005 —
audit_logtable 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 logging —
audit_log()is now async and runs DB writes in a thread pool viaasyncio.to_thread, preventing event-loop blocking on every state-changing request. - UnboundLocalError in AuditService —
log(),list(), andcleanup()no longer crash if the session factory itself raises; session is now guarded withNonechecks in except/finally blocks. - Audit actor for auth events — login, setup, register, and invite-accept
now set
request.state.user_idbefore callingaudit_log(), so the audit trail records the actual user instead of "unknown". - Failed login auditing — failed login attempts now produce a
user.login_failedaudit entry, enabling brute-force detection. - Authorization failure auditing —
require_role()now writes anauth.forbiddenaudit 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 audit —
sandbox.deleteandprovider.deleteonly write audit/log entries when the resource was actually deleted. - Async background cleanup — the periodic cleanup task now uses
asyncio.to_threadfor 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 removedcheckGatewayHealth()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 (replacesshowToast()DOM manipulation)health— gateway connectivity monitoring (replacescheckGatewayHealth()globals)- XSS surface reduced — Alpine's
x-textauto-escapes all dynamic content, eliminating the need for manualescapeHtml()calls in templates. - Render functions removed —
renderGatewayTable(),renderSandboxList(),renderDashboard(), and ~50 otherrenderX()functions replaced by declarative Alpine templates in HTML. app.jsslimmed — reduced from ~340 lines to ~95 lines. Only retainsapiFetch(),showConfirm(),escapeHtml(),formatTimestamp(),navigateTo(), and URL helpers.- WebSocket integration — sandbox detail, logs, and approvals pages receive
live updates via
CustomEventdispatching fromwebsocket.jsto 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
/invitepage 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
/setuppage 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_usedtimestamp tracked on each request. - User management UI —
/userspage for admins with invite form, role badges, and delete actions. Dedicated/users/newand/users/new-service-principalpages 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–004 —
api_keystable,users+service_principalstables with FK constraints, invite token hashing. - CLI commands —
create-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 rewritten —
shoreguard/api/auth.pyexpanded 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-roleattributes.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}/policywas 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 handling —
is_setup_complete(),list_users(), andlist_service_principals()now catchSQLAlchemyErrorinstead of letting database errors propagate as 500s. verify_password()bare Exception catch — narrowed to(ValueError, TypeError)to avoid masking unexpected errors.- WebSocket XSS —
sandboxNamein toast messages is now escaped withescapeHtml(). Log level CSS class validated against a whitelist. delete_filesystem_pathmissing Query annotation —pathparameter now uses explicitQuery(...)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 UPDATElock 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_URLfor container deployments. - Gateway registration API —
POST /api/gateway/registerto register remote gateways with endpoint, auth mode, and mTLS certificates.DELETE /api/gateway/{name}to unregister.POST /{name}/test-connectionto 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-gatewaysCLI command — imports gateways from openshell filesystem config (~/.config/openshell/gateways/) into the database, including mTLS certificates. Replaces the oldmigrate-v2command.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 changes —
POST /create(202 LRO) →POST /register(201 sync).POST /{name}/destroy→DELETE /{name}. Local lifecycle routes (start/stop/restart/diagnostics) return 404 unlessSHOREGUARD_LOCAL_MODE=1. - Request-level logging — gateway register, unregister, test-connection,
and select routes now log at INFO/WARNING level.
LocalGatewayManagerlogs Docker daemon errors, port conflicts, missing openshell CLI, and openshell command failures. api/main.pysplit into modules — extractedcli.py(Typer CLI + import logic),pages.py(HTML routes + auth endpoints),websocket.py(WebSocket handler), anderrors.py(exception handlers).main.pyreduced 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 ofAI_NUMERICHOST. Hostnames that resolve to private/loopback/ link-local addresses are correctly blocked. Includes a 2 s DNS timeout. import-gatewayscrash on single gateway —registry.register()failures no longer abort the entire import; individual errors are logged and skipped.from_active_clustererror handling — missing metadata files, corrupt JSON, and missinggateway_endpointkeys now raiseGatewayNotConnectedErrorwith a clear message instead of rawFileNotFoundError/KeyError.init_db()failure logging — database initialisation errors in the FastAPI lifespan are now logged before re-raising._get_gateway_service()guard — raisesRuntimeErrorif called before the app lifespan has initialised the service (instead ofAttributeErroronNone).- WebSocket
RuntimeErrorswallowed —RuntimeErrorduringwebsocket.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_gatewaysSSRF gap — filesystem-imported gateways were not checked againstis_private_ip(). Now blocked in non-local mode, consistent with the API registration endpoint._import_filesystem_gatewaysskipped count — corrupt metadata JSON was logged but not counted in theskippedtotal, making the summary misleading._import_filesystem_gatewaysmTLS read error —read_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_healthDB error isolation — a database error updating health for one gateway no longer prevents health updates for all remaining gateways.select()implicit name resolution —get_client()was called withoutname=, relying on a filesystem round-trip viaactive_gatewayfile. Now passes the name explicitly.- CLI
import-gatewaysNameError — ifinit_db()failed,enginewas undefined andengine.dispose()in thefinallyblock raisedNameError. - 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 swallowed —
SubprocessError/OSErrorin_docker_start_container/_docker_stop_containerwas 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 page —
inference-providerswas 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_hostinput validation —CreateGatewayRequest.remote_hostis now validated with a hostname regex (max 253 chars) before being passed to subprocess.- SSRF check skipped in local mode —
is_private_ip()checks at connect-time and import-time now allow private/loopback addresses whenSHOREGUARD_LOCAL_MODEis set, since locally managed gateways always run on127.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-keyflag orSHOREGUARD_API_KEYenv 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 Acceptedwith an operation ID. Clients can poll/api/operations/{id}for progress. Includes automatic cleanup of expired operations. forceflag for gateway destroy with dependency checking — prevents accidental deletion of gateways that still have running sandboxes unless--forceis passed.- UNIMPLEMENTED error handling — gRPC
UNIMPLEMENTEDerrors 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
secureflag automatically when served over HTTPS. DEADLINE_EXCEEDEDmapping — gRPCDEADLINE_EXCEEDEDwird jetzt auf HTTP 504 (Gateway Timeout) gemappt.ValidationErrorexception — 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(was201 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 Exceptionauf 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/checkendpoint, 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-Signaling —
asyncio.Eventdurchthreading.Eventersetzt für korrekte Thread-übergreifende Signalisierung. - WebSocket Queue-Overflow —
QueueFull-Exception wird abgefangen mit Fallback aufcancel_event. - Event-Loop-Blocking —
get_client()im WebSocket-Handler mitasyncio.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-Sanitization —
friendly_grpc_error()verhindert, dass rohe gRPC-Fehlermeldungen an API-Clients geleitet werden. - Thread-Safety —
threading.LockfürGatewayService._clientsund thread-safe Reads inOperationStore.to_dict(). - YAML-Parsing-Robustheit —
YAMLError, None- und Skalar-Werte werden inpresets.pyabgefangen. - Metadata-Datei-Robustheit —
JSONDecodeErrorundOSErrorbei 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.
- CLI —
shoreguardcommand with configurable host, port, log level, and auto-reload.