Commit Graph
2470 Commits
Author SHA1 Message Date
Juan Denis ff3c928917 feat(test): passive cgroup-v2 runtime baselines for full installs
Full VM installs and Test Sandbox full mode now record a short
post-install runtime observation (60s settle + 120s sampling) with an
explicit measured/unavailable/failed/interrupted status that never
rewrites install success. Adds the standalone scripts/measure-runtime.py
sampler, the measure-installed-runtime.py harness wrapper, E2E report
rendering, results-API summaries, CI coverage and METRICS.md accounting
boundaries.
2026-09-19 04:52:24 -04:00
github-actions[bot] 46b85b2def chore: bump version to 1.11.6 [skip ci] 2026-09-18 13:28:56 +00:00
Juan Denis 41c079a874 docs(readme): add Vela to Related Projects in the translated READMEs 2026-09-18 01:52:07 -04:00
Juan Denis b548e8a5f3 docs(readme): add Vela to Related Projects 2026-09-18 01:50:20 -04:00
Juan Denis 0aea7e3787 fix(plugins): hot-load extension blueprints on a live app (#144)
Flask refuses register_blueprint once the app has handled its first
request, so on any running panel — where login and dashboard requests
long precede the first extension install — the hot-load in
_register_plugin_blueprint always raised, the installer only logged a
'will load on restart' warning, and the extension's routes stayed
missing. Its pages rendered (frontend is static) while every API call
failed: GETs were swallowed by the tabs' console.error handlers, and the
first POST fell through to the static-file route, surfacing as the 405
reported for the Malware Scanner custom-path scan.

_register_blueprint_live waives Flask's setup guard for the duration of
the registration call. That is safe in this deployment shape: the panel
runs a single gunicorn worker with threads, so the process installing
the plugin is the process serving its routes, and werkzeug rebuilds its
URL matcher lazily on the next bind while in-flight requests keep the
matcher they already bound.
2026-09-18 01:28:59 -04:00
Juan Denis 8837a26af0 fix(logs): keep panel-internal roots out of the log viewer
PR #145 closed the prefix-sibling hole in LogService.is_path_allowed but
left the gap its author named: unlike FileService, the log viewer had no
PROTECTED_ROOTS exclusion. On the install.sh layout the install dir sits
under the allowed root /opt, so the Log Files tab could read — and,
through clear_log, truncate — the backend .env, the SQLite instance dir
and the generated deploy config, the same hole the file manager closed
for GHSA-rm3m-9mvw-68fh.

Share FileService.PROTECTED_ROOTS rather than copying the list, so the
two surfaces cannot drift apart, and reject protected paths before the
allowed-root check. Panel logs live under /var/log and SERVERKIT_DIR,
which are untouched, so legitimate viewing is unaffected.
2026-09-18 01:05:13 -04:00
Juan Denis 8859922b8a Merge pull request #145 from MaxFreedomPollard/fix/log-path-segment-containment
fix(logs): compare whole segments in path guard
2026-09-18 00:59:02 -04:00
Max Freedom Pollard 50d64e9d72 fix(logs): compare whole segments in path guard
LogService.is_path_allowed resolved the requested path with realpath and
then asked whether the result merely started with one of
ALLOWED_LOG_DIRECTORIES. That is a text comparison rather than a
containment test, so a directory whose name only begins with an allowed
root was accepted as if it sat inside it. With the roots shipped at
backend/app/services/log_service.py:30-36, both /opt-private and
/homestead pass on the strength of /opt and /home, which is not what the
comment above the list at line 29 says the list does.

FileService guards the same roots and gets this right at
backend/app/services/file_service.py:96-100, comparing real_path == root
or real_path.startswith(root + os.sep). The same form appears at
backend/app/api/deploy.py:197, backend/app/services/upload_service.py:66
and backend/app/services/linked_panel_agent.py:251. LogService does not,
and its guard also gates truncation through clear_log at
backend/app/services/log_service.py:367.

The loop now compares against the separator-terminated root, so a
sibling sharing a name prefix is rejected while everything genuinely
inside a root still resolves, including paths reached through .. because
realpath has already collapsed them. A regression test covering the
shipped /opt constant, a sandboxed root and the clear_log door ships
with it.
2026-09-15 19:39:37 -04:00
Juan Denis d866de5439 feat(connect): apply the managed presentation profile (plan 25)
Panel side of the managed profile: the panel receives a presentation
profile from Cloud over the existing command channel, stores it,
acknowledges it, and hides or simplifies fleet-owner-only surfaces
until the profile is lifted or the link is revoked.

- ui.managed_profile command handler with managed-profile.json
  storage, audit entries, and ack-by-output; profile is cleared on
  disconnect and on consent revoke
- GET /api/v1/connect/managed-profile for the local UI
- ManagedProfileContext with capability-driven filtering of sidebar
  items, command palette actions, and sidebar settings; ManagedCard
  explains the managed state
- In-page collapses for Templates deploy, About self-update, and
  Backup schedules/storage (restore stays available)
- Settings gains a ServerKit Cloud tab showing link and profile state

Local verification: 153 backend tests pass, 198 frontend node tests
pass, lint clean, build ok. No live policy has been pushed to a real
panel yet.
2026-09-13 03:50:02 -04:00
github-actions[bot] f4c641032a chore: bump version to 1.11.5 [skip ci] 2026-09-13 03:26:48 +00:00
Juan DenisandClaude Fable 5.1 9e320586f3 test(templates): certify an external registry tree, and bring the changelog up to 1.11.4
The catalog certification test now reads SERVERKIT_TEMPLATES_DIR and
SERVERKIT_TEMPLATES_INDEX, so the official template registry's CI can
run the installer's own rules against its tree before a merge publishes
it to every panel. The build_repo_index() check stays bundled-only and
skips under the override.

The changelog had stopped at "1.11.1 - Unreleased" while 1.11.2 through
1.11.4 shipped: the Connect revocation, JWKS refetch, metrics-warning and
sidebar fixes are now recorded under the published 1.11.4 release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JtgcyJ75YEozymcrWDyedu
2026-09-12 22:51:54 -04:00
github-actions[bot] 5e94e3d544 chore: bump version to 1.11.4 [skip ci] 2026-09-12 19:31:12 +00:00
Juan Denis 9d12bf6417 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-09-12 15:11:08 -04:00
Juan Denis 386cd6aa3a fix(connect): stop the client loop when poll mode learns of revocation
_poll_session reports 'revoked' when ServerKit Cloud refuses the poll
with the revoked reason, but the loop only honored 'stopped' and fell
through to re-probe the websocket: ws refused, poll revoked, ws
refused — forever, and the panel never wrote the revoked state. This
is the path a panel behind a close-code-stripping edge depends on, old
builds unconditionally and current ones whenever they are in limited
mode. The poll outcome now gets the same terminal handling as the
websocket one.
2026-09-12 15:09:28 -04:00
Juan Denis 60f8bd8c60 fix(connect): learn revocation from the relay's close-reason frame
The production edge strips non-standard WebSocket close codes, so the
relay's 4009 never reached the panel: a revoked panel kept reconnecting
forever instead of stopping. ServerKit Cloud's relay now sends the
reason as a text frame before closing, and the panel honors it both
during the hello exchange and mid-session, ending the session as
revoked either way. The numeric code still serves direct connections,
and the long-poll fallback's HTTP refusal is unchanged.
2026-09-12 06:31:50 -04:00
github-actions[bot] 6ef6d27e9a chore: bump version to 1.11.3 [skip ci] 2026-09-12 08:46:48 +00:00
Juan Denis 58880b4033 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-09-12 04:39:20 -04:00
Juan Denis fda418d5fa fix(connect): refetch the JWKS when a command arrives signed with an unknown key
ServerKit Cloud mints each signing key the first time it is used and
rotates keys on its own schedule, so a command can legitimately arrive
signed with a key the connection's cached JWKS predates. The panel used
to refuse it as untrusted; it now refetches the JWKS once on an unknown
kid and only refuses a key that is still unknown afterwards.
2026-09-11 22:16:40 -04:00
github-actions[bot] 7b53584045 chore: bump version to 1.11.2 [skip ci] 2026-09-11 23:19:06 +00:00
Juan Denis 1f652adab4 fix(connect): surface silent metrics collection failures
A metrics build that keeps raising, and a sampler that keeps producing
nothing, both used to log at debug level: ServerKit Cloud showed no
telemetry and the panel had nothing to act on locally. A failing build
now warns on the first failure of a streak and logs its recovery, and
five consecutive empty collects warn that the panel sampler may not be
running. The healthy path stays quiet.

Raises the test-count floor to 5271 (check_test_count.py --update): the
three new publisher tests plus the floor never having been bumped for
the 63 cases added since the last update.
2026-09-11 19:08:37 -04:00
Juan Denis fc5a041764 Fix first-run sidebar preferences and extension loading [skip version] 2026-09-11 00:17:39 -04:00
Juan Denis fb0be688dd chore: prepare 1.11.1 release [skip version] 2026-09-10 22:23:42 -04:00
Juan Denis 2fd6f3c97c test(settings): await desktop footer actions before measuring [skip version] 2026-09-10 22:11:35 -04:00
Juan Denis b181e32674 fix(connect): enforce consent and recover bounded log delivery [skip version] 2026-09-10 22:05:21 -04:00
Juan Denis b90931317c fix(ci): isolate dev release publication from PR builds [skip version] 2026-09-10 22:05:17 -04:00
Juan Denis 693ba8e322 docs: update demo links in all README translations [skip version] 2026-09-10 22:05:14 -04:00
Juan DenisandClaude Fable 5.1 3f60148214 feat(connect): publish inventory, diagnostics and bounded logs to Cloud [skip version]
connect_inventory builds inventory/1 (applications keyed on the row id so a
rename is not a new application; slug, branch and commit instead of a repo
URL; a WordPress admin URL only where this panel installed it; databases as
names, engines and states) and diagnostics/1 (busiest processes and mounts,
never a command line). connect_logs tails the panel and web error logs by
offset with a generation per rotation, reads application output and build
logs through the existing services, applies every Cloud bound locally,
redacts before sending, and stops on collect: false.

The relay client publishes inventory on connect and every five minutes,
diagnostics each minute while Cloud allows it with a fifteen-minute back-off
after a refusal, and logs every five seconds while sources are wanted.
Older Clouds keep answering the facts stream as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JtgcyJ75YEozymcrWDyedu
2026-09-10 21:35:18 -04:00
Juan DenisandClaude Fable 5.1 e6cac95f0f ci(release-smoke): publish every dev push as a pre-release for the Cloud release lab [skip version]
The smoke job already builds the release tarball on every push to dev and
threw it away. It now names the build v<VERSION>-dev.<sha7>, writes that
into VERSION so the installed panel reports it, and publishes the tarball
and checksums as a GitHub pre-release. The three newest dev builds are
kept; older ones and their tags are removed. Pull-request runs are
unchanged. The ServerKit Cloud release lab lists these rows as dev builds
and can install and upgrade-test a commit before it is promoted to main.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VndLQNwYw4vYNxQhAFosHY
2026-09-10 21:35:12 -04:00
Juan Denis 4dc281ce58 test(settings): await both mobile footer actions [skip version] 2026-09-08 09:40:06 -04:00
Juan Denis 0fc47c3d3e fix(ai): normalize usage quota scope IDs [skip version] 2026-09-08 09:34:35 -04:00
Juan Denis 172e410ce3 chore: prepare 1.11.0 and refresh release inventories [skip version] 2026-09-08 09:31:21 -04:00
Juan Denis 8468ab79f2 test(ai): verify management UI and document completed integration 2026-09-08 04:10:21 -04:00
Juan Denis 7d5623bccb feat(settings): add connected AI model policies and usage views 2026-09-08 04:09:54 -04:00
Juan Denis bebc69f71c feat(ai): connect task routing, guarded fallback, and scoped run accounting 2026-09-08 04:09:27 -04:00
Juan Denis 6a77f5e5ae docs(skills): revise pull request description guidance 2026-09-08 03:05:19 -04:00
Juan Denis 9bb474e111 docs(ai): document settings standards and Prompture integration review 2026-09-08 03:03:58 -04:00
Juan Denis 891666786c test(settings): cover AI controls and responsive settings surfaces in browsers 2026-09-08 03:03:43 -04:00
Juan Denis 8e9c4fa6b0 fix(settings): unify forms, notifications, and AI configuration controls 2026-09-08 03:03:19 -04:00
Juan Denis 54028fbff4 fix(ai): enforce assistant enable state and conversation cost limits 2026-09-08 03:03:00 -04:00
Juan Denis 5a4036e9b2 fix: resolve credential draft and backup fallback review [skip version] 2026-09-08 01:39:54 -04:00
Juan Denis fde0acee6d fix(ai): satisfy release gates and prepare 1.10.0 [skip version]
Keep connection persistence behind the service boundary, use typed errors and shared JSON parsing, and refresh reviewed inventories. The new GET connection catalog accepts API keys so API clients managing connections can discover IDs without exposing credentials; existing JWT-only routes stay unchanged. Add regression coverage and release notes.
2026-09-08 01:30:03 -04:00
Juan Denis add2f713a5 feat(ai): use Prompture 1.11 native gateway streaming 2026-09-08 00:47:58 -04:00
Juan Denis 569b052659 feat(ai): expose Prompture providers and saved connections 2026-09-08 00:42:54 -04:00
Juan DenisandClaude Fable 5.1 55291c3c10 fix(update): stop snapshotting the SQLite DB twice per update; test vendor shim MIME
The tree backup rsync copied backend/instance/serverkit.db even though the
same database had just been written as the pre-upgrade copy, and rollback
only ever restores from that copy. On a 450 MB database that made every
update cost ~900 MB of backups, which is how small VPSes kept filling up.
Exclude the SQLite files from the tree snapshot. Retention is unchanged
(SERVERKIT_BACKUP_RETENTION, default 3, plus the 15% disk budget).

Add a test that every shipped nginx vhost forces a JavaScript MIME type for
/serverkit-vendor/*.mjs and that the regex cannot capture the
/api/v1/plugins/<slug>/assets/... route.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWc84WDsWR7M55Mn7qtpie
2026-09-08 00:27:42 -04:00
Juan DenisandClaude Fable 5.1 bbce447e85 fix(nginx): serve /serverkit-vendor/*.mjs shims as text/javascript
Stock nginx (1.18-1.24 on Debian/Ubuntu) has no `.mjs` entry in mime.types,
so the runtime-extension vendor shims the import map points at came back as
application/octet-stream. Browsers refuse to execute an ES module with that
type, so every runtime-loaded extension (WordPress, Git, Mail, Fail2ban, ...)
failed on fresh installs with "Failed to fetch dynamically imported module:
blob:..." (Chrome) or "'application/octet-stream' is not a valid JavaScript
MIME type" (Safari).

Add a location block to the shipped vhosts (and the container frontend
config) that forces text/javascript for /serverkit-vendor/*.mjs. The regex is
anchored to that folder: a bare `\.mjs$` would also capture the
/api/v1/plugins/<slug>/assets/dist/index.mjs route and 404 every bundle.
update.sh already re-copies these vhosts, so existing installs pick it up on
their next update.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWc84WDsWR7M55Mn7qtpie
2026-09-08 00:25:25 -04:00
Juan Denis 1ace946ab9 Keep service metrics stable across metadata refreshes [skip version] 2026-09-07 23:34:57 -04:00
Juan Denis 116f817197 Clarify PR description guidance
Refines the create-pr skill to keep PR bodies concise and structured. Large changes should use a short body with Highlights, Upgrade notes, and Validation outside the accordion, while implementation details stay inside the technical appendix.
2026-09-07 23:33:13 -04:00
github-actions[bot] 3ca9255d81 chore: bump version to 1.9.29 [skip ci] 2026-09-08 03:14:16 +00:00
Juan Denis d5577e8bda fix(logs): keep remote runtime lookups off the panel host 2026-09-07 22:46:16 -04:00
Juan Denis 0f32593a38 Raise backend test baseline for PR #136/#139 tests 2026-09-07 20:11:45 -04:00