Cluster G of plan 38 (plan 32) + integration glue.
- gpu: complete the core-slim move — remove core app.api.gpu +
app.services.gpu_service (+ its blueprint registration), rebuild the probe in
the serverkit-gpu builtin extension (nvidia-smi parser, Linux-guarded)
- __init__: register dns_cutover + setup_health blueprints
- doctor_service: add fleet DNS-vs-IP checks, backup-proof checks
(backup_drill_stale/backup_unverified), and the setup-health section into run()
- builtin_handlers: seed the host-doctor sweep + weekly setup-health nag
- test_jobs: schedule count 15 -> 17 for the two restored schedules
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cluster C of plan 38 (plan 31). dns_cutover_service reconstructed from readable
string fragments + plan doc + the frontend contract; SSH importer from the
surviving wiring tests.
- dns_cutover_service: server-sourced snapshots, dry-run + real cutover via the
guarded provider client, world-restoring revert that deletes cutover-created
records (uses restored DnsCutoverSnapshot) — 20 tests green
- api/dns_cutover: 7 JWT/admin-gated endpoints matching dnsCutover.js
- site_importers/ssh: GenericSshImporter + parse_ssh_source, keyfile-only,
credential-scrubbing; wired into site_import_service ('ssh' source) — 33 tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cluster A of plan 38 (plans 27/28). survey_service reconstructed from the
surviving test + api/survey.py caller (bytecode corrupt); fleet_repair_service
recovered faithfully from readable bytecode; fleet_doctor_service reconstructed
from the fleet contract + FleetDoctorResult model (bytecode corrupt).
- survey_service: probe catalog, run_survey/normalize_map/diff_maps over the
agent registry, ServerSurvey snapshots (6 tests green)
- fleet_doctor_service: allowlisted fleet health sweep writing one
FleetDoctorResult per (server_id, check_key) + DNS-vs-IP checks
- fleet_repair_service: executes the doctor's allowlisted repair actions
(audited, capability-gated) — 20 proving tests green
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1 of the July-5-7 recovery rebuild (plan 38). Backend now boots.
- models: reconstruct DnsCutoverSnapshot (from bytecode) + FleetDoctorResult
(from migration 063 DDL); restore lost columns applications.ports/
bootstrap_done, app_volumes.declared_size, dns_provider_configs.origin_ca_key
- migrations: recreate the 15 missing revisions 056/057/060/062/063/064/065/
067/068/069/070-074, reconciling the chain to a single linear head (074).
add_column revisions guard on live schema so a fresh-DB upgrade reaches head
- rbac: restore _APP_ROLE_RANK + require_workspace_access/require_workspace_role/
app_access_tier/require_app_member (plan 19/29 4-tier authz), recovered from
bytecode; unblocks api/cron.py import
- services: reconstruct cron_run_service.CronRunService (record_run/recent_runs/
stats, edge-triggered failure/recovery transitions) from bytecode
Gate: create_app() imports clean, db.create_all() OK, flask db upgrade on a
fresh SQLite DB reaches head 074 (linear chain).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves Safety scan DoS advisories SFTY-20260626-04169 (socketio) and
SFTY-20260626-60054 / SFTY-20260626-39072 (engineio). Verified compatible
with Flask-SocketIO 5.6.1 under async_mode='threading'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regenerate the pre-bundled frontend/src/plugins copy from its
builtin-extensions source (node scripts/sync-builtin-frontends.mjs) to clear the
builtin-frontend drift CI check. Content-identical re-serialization; no
behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add manifest UI: the repo-import wizard shows a multi-service summary when a
version:1 serverkit.yaml is detected; Service detail Settings gains a Manifest
section (managed-by badge, status pill, scaffold download, Plan/Apply); the
project view shows a manifest status strip. New api/manifests.js client module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Route manifest-created apps to a fleet server via top-level/per-service server:
(sets Application.server_id; unknown server = plan issue). Clone the
manifest-resolved config (env + per-preview overrides + domain) into non-WP PR
previews via ManifestPreviewService. Add 'serverkit manifest plan|apply|diff'
CLI verbs against the manifests API.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both webhook receivers now re-read serverkit.yaml after a deploy: unchanged hash
is a no-op, a changed manifest auto-applies its autoDeploy services and flips the
rest to pending with an admin notification. Register a 'manifest' drift check
that projects live state onto the declared surface (in_sync iff declared items
match; repair re-applies). Emit manifest.applied/pending/drifted/error events.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add EnvironmentVariable.value_from (migration 055) so an env var can resolve from
a vault secret or a sibling service's connection property at injection time — the
value never lands in the row, masking stays intact, and a rotated secret
propagates on next deploy. EnvReferenceResolver handles both; the apply engine
plans set_env_ref steps, flags missing secrets as plan-time issues, and generates
a random vault secret for generate:true (idempotently).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add manifest_apply_service (resolve -> ordered plan -> idempotent apply inside a
manifest.apply DeploymentJob with before/after snapshots), covering app create/
update, env, volumes, managed databases, backup policies and domain attach.
Extract the WordPress one-call domain+SSL pattern into a core DomainAttachService
(DNS -> vhost -> best-effort cert, each stage degrading to a warning). Wire the
/api/v1/manifests blueprint with get/plan/apply (admin-gated, audit-logged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stop dropping what import already detects. Add Application.healthcheck_path
(migration 054) and an application_manifests table (one row per project) storing
the normalized manifest, hash and provenance. Import now seeds non-secret env
values and records the health-check path, and the zero-downtime restart gate
polls that path instead of a blind sleep.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the declarative serverkit.yaml v1 spec: JSON Schema + reference doc, a pure
parse/validate/normalize service, version:1 recognition in the repo manifest
detector (legacy flat files unchanged), and a scaffold endpoint that renders a
manifest from a live app.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Validated the engine against a real public box (DO, port-25 egress open) in
addition to WSL. Documents two findings for the next iteration:
- setup can be completed programmatically: in bootstrap GET /api/account grants
sysBootstrapGet/sysBootstrapUpdate, so the panel could auto-finish Stalwart's
one-time setup rather than requiring the web wizard
- the management vocabulary (principal/dkim/queue/mailbox/domain) was confirmed
against Stalwart's own config schema, so the EP_* nouns are correct
No behavior change — comment only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the real container and drove its API; corrected what only a live box
reveals:
- image is stalwartlabs/stalwart:latest — the assumed stalwartlabs/mail-server
no longer resolves on Docker Hub (404 on pull)
- config lives at /etc/stalwart/config.json and data at /opt/stalwart, so
install() now bind-mounts two host dirs instead of one /opt/stalwart-mail
- admin credential is pinned via a single STALWART_RECOVERY_ADMIN=user:secret
env var (not FALLBACK_ADMIN_USER/SECRET)
- a fresh container boots into BOOTSTRAP mode: only the /account setup UI is up
and every /api/* returns 404 (RFC-7807 problem+json) until the one-time setup
completes. get_status now probes /api/session and reports needs_setup +
setup_url; the Overview UI shows a "finish setup" banner. Verified live:
GET /api/session -> 404 problem+json.
- _api parses the problem+json detail/title error shape
Tests updated to the real install argv + two new cases (bootstrap needs_setup,
problem+json parsing): 58 pass. Post-setup CRUD sub-paths (/api/principal etc.)
remain best-effort constants until confirmed against a fully-initialized
instance on a real box.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 hermetic tests (docker/HTTP/socket/DNS-provider all stubbed; pass on
Windows). Cover: manifest validity + lifecycle-key/jobs wiring (guards the
loader-key bug), models + no mailbox-password column, Stalwart install argv
(admin API loopback-only, mail ports, generated+persisted admin secret) and
Windows gating, preflight pass/block + persistence, the sending-activation
gate (refused without a passing preflight, force override audited), DKIM
keygen + exact SPF/DKIM/DMARC/MX/A record push with source='mail', fail2ban
jail, and blueprint auth/validation/503 paths.
CHANGELOG: document the Mail Server extension under Unreleased.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-contained multi-tab Mail Server page mirroring the serverkit-dns-server
plugin shape (named MailPage export, no default export; own PageTopbar tabs;
generic api.request calls). Tabs: Overview (status/install/service + a
prominent deliverability preflight banner that makes clear sending is blocked
until PTR/port-25/RBL pass), Domains, Mailboxes (+ autoresponder), Forwarders,
DNS & DKIM (generate/deploy records + copy-to-clipboard + LE cert), Queue.
Canonical source under builtin-extensions/serverkit-mail/frontend/ with a
byte-identical synced copy (+ manifest) under frontend/src/plugins/serverkit-mail/.
SCSS-only, host components reused, no inline styles. Verified: eslint 0 errors,
npm run build succeeds (Vite compiles the plugin).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- plugin.json: lifecycle hooks were keyed on_install/on_uninstall, but the
loader reads lifecycle.get('install')/('uninstall') — the hooks (notify-event
registration, container teardown) would have silently never fired. The
manifest validator only vets the values, not the keys, so this slipped past.
- stalwart_service.get_status: read the engine version from a dedicated
read-only /status endpoint instead of GET-ing the /reload endpoint.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A missing route used to surface as a bare "Request failed" toast; now
it reads e.g. "Request failed (405): /api/v1/email/dns-providers" so
the broken endpoint is diagnosable from the UI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The /api/v1/email/dns-providers routes lived only in the serverkit-email
extension, so panels without it (email is gated to mail users) got a
404/405 from the Settings -> Connections DNS tiles. The service was
already core; the routes now are too, at the same historical paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regenerated via scripts/sync-builtin-frontends.mjs; fixes the Extensions CI
drift check that failed after plan 15 added these extensions without
re-running the sync.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Blueprint/model/job registrations and schedule seeding for drift,
doctor, support bundle, bandwidth, FIM, malware-scan and htaccess
tools; API module aggregation, SCSS imports and CHANGELOG for the
batch. Full backend suite: 1793 passed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>