a31cc5d3 widened the jwt-only census to app/plugins and shipped a ceiling
of 611 measured on a dev box. CI measured 591 and went red: most
app/plugins/ live copies are GITIGNORED, so the count depended on which
extensions a machine happened to have installed. That is the same
machine-dependence the builtin-copy drift census exists to prevent, and
this walked straight into it.
Count builtin-extensions/ instead -- the source, pinned equal to the live
copy by test_builtin_extension_drift.py, and tracked. Filter to
git-tracked files as well, because extractions leave stale ignored
directories behind (cloudflare-ops, git, localkit after plan 52) that
would otherwise be counted on some working copies but not others.
Stable population: 608 (35 of them extension routes across
cloud-provision, remote-access and status). Also regenerates
docs/MIGRATION_INVENTORY.md, whose row is derived from this ceiling, and
points the CLAUDE.md / AGENTS.md notes at the scope actually scanned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #122 review suggested converting the serverkit-status routes from
@jwt_required() + an inline is_admin check to rbac decorators. Declined:
plan 76's recorded decision (2026-08-19) is that JWT-only is the
deliberate default, because every rbac decorator authenticates through
auth_required() and therefore GRANTS API-key callers access -- an
expansion of the authenticated surface, never a mechanical sweep.
Two gaps let that suggestion look uncontroversial:
1. jwt_only_census.py scanned only app/api, so the extension's 15 bare
@jwt_required() routes were invisible to the ratchet. It now scans
app/plugins as well; ceiling 577 -> 611 for the newly visible
population (a scope widening, not a regression). Only the live copy
is counted -- builtin-extensions/ is the same code twice and the
drift guard pins them equal.
2. Neither CLAUDE.md nor AGENTS.md said the inline shape is already a
valid explicit decision (test_route_authz_static counts .is_admin via
ATTRIBUTE_GATES), or that ~91 routes across 21 files use it. Both now
do, with the plan 76 quote and a note that automated reviewers raise
this routinely.
Audited 2026-08-31 by AST over app/api + app/plugins + builtin-extensions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both are now enforced by tests that fail the build, so they belong where the
"adding a feature" checklist is rather than in a commit message nobody
re-reads. The one-boundary rule is the trap this round fell into: stacking
`@jwt_required()` outside a policy decorator looks like belt-and-braces and
is actually a regression, because it rejects API-key callers before the
policy can authorize them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Issue #96: an operator fronting the panel with Caddy and a smallstep private CA
hit a redirect loop, and there was no documented path to follow. The
architecture already tolerated this deployment -- the fix was a config file we
already ship -- but nothing said so anywhere.
Adds an INSTALLATION.md section covering both topologies (external proxy ->
ServerKit nginx, full features, HOPS=2; external proxy -> the app directly,
panel-only, HOPS=1), why the loop happens, how to count TRUSTED_PROXY_HOPS,
Caddy (with acme_ca for a private CA), Traefik, Nginx Proxy Manager and plain
nginx snippets, what a private CA does and does not require (the panel never
sees it; a remote agent's host must trust the root), and a symptom-to-cause
table for the loop, stale HSTS, the rejected websocket and wrong client IPs.
Also corrects the Docker documentation, which had drifted badly:
- README (en/es/pt/zh-CN), INSTALLATION and DEPLOYMENT all told people to run
`docker compose up -d` and reach the panel at http://localhost. It starts the
all-in-one container on :5000, and a containerised panel cannot manage its own
host -- now stated up front rather than discovered.
- DEPLOYMENT is a host-install guide that told operators to run
`docker compose build && up` to fix a panel, which would build a second panel
beside theirs, and carried a certbot cron copying certs and restarting a
container that does not exist on a host install.
- CLAUDE.md, AGENTS.md, CONTRIBUTING.md and LOCAL_DEVELOPMENT.md (x3) pointed at
a docker-compose.dev.yml that has never existed; the snippet offering to
create it referenced a frontend/Dockerfile.dev that does not exist either.
Every anchor introduced here was checked to resolve.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
File counts were roughly a third of reality (api 36 vs 90, services 48 vs 165,
models 15 vs 80, pages 29 vs 60), so both files understated the codebase enough
to mislead anyone sizing a change.
Two claims were wrong rather than stale. `db.create_all()` is never called —
schema is 74 Alembic migrations, and describing tables as auto-created invites a
model change that ships without one. And the Production Build section
recommended GeventWebSocket workers while the deployment note 25 lines above
warned against exactly that; the Dockerfile and systemd unit both use a plain
threaded worker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduces a new `.coderabbit.yaml` with ServerKit-specific review guardrails, and expands env/security documentation for trusted proxy headers and per-IP login brute-force throttling. It also adds inline backend notes clarifying `request.remote_addr` behavior under ProxyFix for auth, cron, and pairing paths, and updates the frontend plugin manifest to remove the Tramo and K8s plugin entries.
The app runs flask-socketio with async_mode='threading' (the backend
is real-thread based: jobs scheduler, queue bus, agent registry), where
engineio serves WebSocket via simple-websocket on the raw socket. The
systemd unit template and Dockerfile still launched gunicorn with the
GeventWebSocketWorker, which ALSO answers the upgrade — the client got
HTTP/1.1 101 twice and read the second one as a frame ('Invalid frame
header'), so every panel silently degraded to polling. Proven by
hexdump (double 101, first 'frame' byte 0x48 'H') and fixed live:
single 101 + 0x81 text frame, socket.io connects through Cloudflare.
- unit template + Dockerfile: gunicorn -w 1 --threads 100 (single
PROCESS stays mandatory — in-memory agent gateway)
- requirements: pin simple-websocket explicitly (was transitive-only)
- CLAUDE.md / ARCHITECTURE.md / SECURITY.md single-worker notes updated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dev was ~149 commits ahead of the last release with no changelog and several
stale docs. This brings the user-facing docs in line with what actually ships.
New:
- CHANGELOG.md — curates the unreleased panel work (agent fleet, plugins,
status pages, cloud provisioning, RHEL support, installer hardening).
- agent/CHANGELOG.md — the agent release workflow links to this on every
published release; it was a broken link. Now documents the agent's
independent release line.
- SECURITY.md — vulnerability reporting policy + agent trust model
(agent.key is host-equivalent; Features.Exec off by default; single-worker).
Fixes:
- Stale version stamps: docs/README.md and docs/API.md said 0.9.0 -> 1.6.7.
- Supported OS: README/INSTALLATION said Ubuntu/Debian only; installer also
covers Fedora and RHEL/Rocky/AlmaLinux.
- Dead links/placeholders: API.md wiki link, INSTALLATION.md "yourusername".
- Windows agent + short-code pairing were undocumented; added to README,
agent/README.md, and docs/pairing.md (incl. corrected host-stable
credential-key derivation — USERNAME was dropped in agent 1.6.14).
- Documented the single gevent-websocket-worker deployment constraint in
CLAUDE.md and docs/ARCHITECTURE.md.
- CONTRIBUTING.md: removed reference to a non-existent frontend test suite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce fallback audit logging and enrich audit tooling; add builtin extension discovery/install; and update default launcher ports and related docs/config.
Key changes:
- New middleware app.middleware.audit registers an after-request fallback that records authenticated mutating API requests when routes don't emit explicit audit logs.
- Audit model/action constants extended and AuditService enhanced (request context handling, redaction for sensitive settings, optional commit flag, sets g.audit_logged, emits events).
- Routes/services updated to create explicit audit entries for marketplace and plugin operations (create/update/publish/install/uninstall/enable/disable) and SSO/settings flows switched to use AuditService.
- Added tests for audit logging behavior and redaction (backend/tests/test_audit_log.py).
- Plugin service: added BUILTIN_EXTENSIONS_DIR, list_builtin_extensions and install_builtin_extension; plugin API endpoints to list/install builtin extensions.
- Contribution system extended to support layouts and route layout metadata; frontend wiring updated to surface standalone/custom layouts and extension routes.
- Launcher port defaults changed (backend -> 47927, frontend -> 41921). Updated backend .env.example, config CORS defaults, Dockerfiles and docker-compose to use SERVERKIT_BACKEND_PORT, and many docs/README files and frontend envs to reflect new ports.
- Minor frontend cleanups and new builtin extension example (serverkit-git) plus associated manifest and frontend re-export.
These changes improve observability/security for mutating APIs, enable opt-in builtin plugins, and align local dev/run ports with the launcher defaults.
Add a unified, cross-platform development launcher and update docs/styles to SCSS.
- Add dev/dev.sh: comprehensive Bash dev launcher with stable non-default ports, port resolution/cleanup, WSL-aware behavior, ngrok support, and validation tooling.
- Rewrite Windows entrypoint (dev.ps1) to require WSL and delegate to dev/dev.sh; make top-level dev.sh a thin wrapper that execs dev/dev.sh.
- Add AGENTS.md with project overview, architecture, and development instructions for assistance tools.
- Update docs (CLAUDE.md, CONTRIBUTING.md) to reflect migration from LESS to SCSS and update style guidance.
- Update .gitignore to ignore backend/.venv-wsl, frontend.env.development.local, and /.agents.
- Frontend: add new pages and page-specific SCSS files, update main.scss, components, pages, client/socket services, and vite.config.js to support the SCSS-based frontend changes.
These changes unify local development workflows (especially via WSL), improve port stability, and complete the frontend styling migration from LESS to SCSS.
Add cross-platform development launchers (dev.sh, dev.ps1) and a frontend ESLint flat config; remove legacy scripts and update docs to use ./dev.sh. Harden backup and storage logic: require admin for backup endpoints, use realpath checks to ensure backup paths stay inside the backup dir, prevent path traversal during archive extraction, and generate schedule IDs with UUIDs. Improve StorageProviderService with masked-secret handling, set restrictive file perms on config, validate S3/B2 endpoint URLs to mitigate SSRF (urlparse + ipaddress), avoid following symlinks during uploads, and adjust MD5 usage. Update Bandit config to skip expected noisy checks and tweak .gitignore entries and contributing docs to target the dev branch and the new dev scripts.
Introduces a multi-step onboarding wizard for initial server setup, including admin account creation, use case selection, resource tier display, and summary. Adds backend support for onboarding completion and use case storage. Implements developer mode toggle in settings, exposing an icon reference panel. Updates styles and refactors related frontend logic for setup and settings pages.