Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
af777a8
feat(marketplace): default the registry to the public serverkit-exten…
jhd3197 Jul 3, 2026
09781a1
refactor(marketplace): strip dashboard chrome from the Browse tab
jhd3197 Jul 3, 2026
53220e5
fix(plugins): stamp source_type='registry' on registry installs
jhd3197 Jul 3, 2026
e65f4e2
feat(marketplace): merge installed tabs and move manual install into …
jhd3197 Jul 3, 2026
fe9e924
polish(marketplace): extension copy sweep, publisher filter chips, do…
jhd3197 Jul 3, 2026
4c76d43
feat(security): cloud-metadata egress guard for app containers
jhd3197 Jul 3, 2026
e7e2de4
feat(monitoring): on-demand server speed test card
jhd3197 Jul 3, 2026
9edeb68
feat(apps): first-class per-app CPU/memory limits
jhd3197 Jul 3, 2026
bd76241
feat(auth): one-time login links and demo mode
jhd3197 Jul 3, 2026
0a82873
feat(databases): live processlist with kill in the explorer
jhd3197 Jul 3, 2026
1fb7b81
feat(databases): curated engine config tuner
jhd3197 Jul 3, 2026
76a688f
feat(databases): managed DB users and one-click Adminer SSO
jhd3197 Jul 3, 2026
d01d820
feat(imports): panel-migration pipeline with cPanel, DirectAdmin and …
jhd3197 Jul 3, 2026
64658cf
feat(wordpress): pull-import a live site over SSH
jhd3197 Jul 3, 2026
91e6f5c
feat(panel): wire imports, database tools and quick wins into the app
jhd3197 Jul 3, 2026
f296e1d
feat(ops): config drift detection, per-resource repair and doctor sweep
jhd3197 Jul 3, 2026
c3f356b
feat(cli): API-backed operator verbs and diagnostic support bundle
jhd3197 Jul 3, 2026
c192e4f
feat(security): job-backed malware scans with a web-shell YARA pass
jhd3197 Jul 3, 2026
202b310
feat(security): scoped file-integrity monitoring
jhd3197 Jul 3, 2026
ce6d0b1
feat(extensions): CrowdSec integration extension
jhd3197 Jul 3, 2026
3cbbcb5
feat(hosting): per-domain bandwidth accounting
jhd3197 Jul 3, 2026
96d162e
feat(hosting): per-site nginx micro-cache toggle
jhd3197 Jul 3, 2026
8ff893a
feat(hosting): .htaccess to nginx converter
jhd3197 Jul 3, 2026
e3b69da
feat(extensions): authoritative DNS server extension
jhd3197 Jul 3, 2026
5e22aa8
feat(panel): wire drift/doctor, CLI tools, security suite and hosting…
jhd3197 Jul 3, 2026
6e95430
chore: bump version to 1.7.12 [skip ci]
github-actions[bot] Jul 4, 2026
b35a41b
chore(plugins): sync pre-bundled builtin frontends (crowdsec, dns-ser…
jhd3197 Jul 4, 2026
03ede7b
Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev
jhd3197 Jul 4, 2026
145d6b1
chore: bump version to 1.7.13 [skip ci]
github-actions[bot] Jul 4, 2026
75a839f
fix(dns): move DNS provider routes back to core
jhd3197 Jul 4, 2026
df63abe
fix(frontend): name endpoint and status in fallback API error
jhd3197 Jul 4, 2026
2f36fa4
feat(mail): serverkit-mail extension backend (Stalwart engine)
jhd3197 Jul 4, 2026
f428ac1
fix(mail): correct lifecycle manifest keys and status version probe
jhd3197 Jul 4, 2026
d0d55fc
feat(mail): serverkit-mail frontend tab-group UI
jhd3197 Jul 4, 2026
d99d4db
test(mail): proving suite for serverkit-mail + changelog
jhd3197 Jul 4, 2026
427492c
fix(mail): pin Stalwart engine against live 0.16.11 (WSL+Docker)
jhd3197 Jul 4, 2026
f3a1f09
docs(mail): record live Stalwart 0.16 findings from real-box validation
jhd3197 Jul 4, 2026
8c5ede0
feat(manifest): v1 serverkit.yaml spec, normalizer, scaffold (Phase 0)
jhd3197 Jul 4, 2026
ae0f823
feat(manifest): persist manifest + seed env + health-check gate (Phas…
jhd3197 Jul 4, 2026
315c80e
feat(manifest): apply engine — resolve/plan/apply + core domain attac…
jhd3197 Jul 4, 2026
f823c6c
feat(manifest): fromSecret/fromService/generate reference binders (Ph…
jhd3197 Jul 4, 2026
8d61abb
feat(manifest): push re-sync, drift check family, events (Phase 4)
jhd3197 Jul 4, 2026
e1f9d5b
feat(manifest): fleet targeting, manifest-powered previews, CLI verbs…
jhd3197 Jul 4, 2026
57ca861
feat(manifest): wizard summary, Settings section, project status stri…
jhd3197 Jul 4, 2026
5fe5473
chore: bump version to 1.7.14 [skip ci]
github-actions[bot] Jul 4, 2026
0fef682
chore(plugins): re-sync bundled serverkit-mail plugin.json
jhd3197 Jul 4, 2026
3c34bd6
Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev
jhd3197 Jul 4, 2026
79bd032
chore: bump version to 1.7.15 [skip ci]
github-actions[bot] Jul 4, 2026
9e68202
fix(deps): bump python-socketio to 5.16.3 and python-engineio to 4.13.3
jhd3197 Jul 4, 2026
07baefb
chore: bump version to 1.7.16 [skip ci]
github-actions[bot] Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,99 @@ awaiting a stable release:

### Added

- **Mail Server extension (`serverkit-mail`)** — an opt-in built-in that runs a
self-hosted mail server (Stalwart, in a managed Docker container) driven
through its HTTP admin API. Manages mail domains, mailboxes, forwarders,
autoresponders and catch-all; generates DKIM keys and deploys DKIM/SPF/DMARC/MX/A
records through the existing DNS-provider integrations; requests a Let's Encrypt
cert for the mail hostname; and shows the outbound queue. A **deliverability
preflight** (reverse-DNS/PTR match, port-25 egress, RBL listing, listening
ports) runs on a daily schedule and **blocks outbound sending until it passes**
(an explicit force override is audit-logged), because self-hosted mail on a VPS
is a real commitment — many providers block port 25 and a fresh IP can be
pre-burned. Ships brute-force auth jails and registers the mail store for
scheduled backups. Not installed by default; enable it from Marketplace. It runs
alongside the older Postfix/Dovecot `serverkit-email` extension with entirely
separate identifiers.
- **Configuration drift detection + repair ("doctor")** — a daily read-only
sweep re-renders the expected nginx vhost and compose override for every
managed resource from panel state and diffs it against disk; drift raises an
admin notification, and a Doctor tab on Monitoring runs the full diagnosis
(drift, core services, cert expiry, disk headroom, database) with
diff-confirmed per-item repair and batch repair. Nothing is ever changed
automatically.
- **Operator CLI** — the `serverkit` CLI gains API-backed verbs for when the
browser isn't an option: `status`, `services list/restart`, `apps list`,
`doctor [--repair]`, `repair <type> <id>`, `update`, `support-bundle`, and
`login-url` (mints a one-time login link). Auth is a break-glass 10-minute
token minted in-process — root on the box already implies full control —
and every mint is audit-logged.
- **Diagnostic support bundle** — one call (API or CLI) exports a sanitized
zip (versions, service states, setting shapes without values, recent job
failures, scrubbed log tail) to attach to a bug report; secrets are
scrubbed by pattern and by the settings secret-key list.
- **Web-shell/YARA scanning + job-backed malware scans** — malware scans now
run as jobs, can target an app's docroot directly, and a curated web-shell
rules pass (eval/base64 droppers, c99/r57/WSO markers, PHP-in-image,
auto_prepend hijacks…) runs alongside ClamAV — with a pure-Python fallback
when yara isn't installed, custom rule upload, and one-click quarantine
with restore.
- **File-integrity monitoring** — baseline-and-diff over the paths ServerKit
manages (nginx config, ServerKit systemd units, app docroots on opt-in)
every six hours, feeding the Notifications Bus; the Security → Integrity
tab gains baseline/check/accept controls and a what-changed view.
- **CrowdSec extension** — a new `serverkit-crowdsec` marketplace extension
surfaces CrowdSec decisions and alerts, lets you ban/unban IPs and manage
allowlists via cscli, with graceful degradation when CrowdSec isn't
installed.
- **Authoritative DNS server extension** — a new `serverkit-dns-server`
marketplace extension runs PowerDNS in a managed container so a ServerKit
box can be the nameserver for its domains: zones, records, DNSSEC with DS
records for the registrar, and a delegation check. For homelab and
air-gapped setups; complements (never replaces) the provider integrations.
- **Per-domain bandwidth accounting** — daily nginx access-log aggregation
into per-site transfer stats, with 30-day sparklines on the Services list
and a monthly figure on the service Overview.
- **Per-site micro-cache** — an opt-in nginx micro-cache (10s TTL) per site
with safe bypasses for logged-in/admin/cart cookies and paths, an
`X-SK-Cache` header for verification, and a manual purge button. Big cheap
win for WordPress/PHP sites.
- **`.htaccess` → nginx converter** — a paste-in tool (on the Import wizard)
translating common rewrite/redirect/auth/access rules to nginx directives,
flagging anything it can't translate with the reason and line number.
- **Import a site (migration pipeline)** — a 5-step wizard at `/imports`
(entry points on Services and WordPress) restores cPanel/WHM, DirectAdmin
and Hestia/Vesta backup archives onto ServerKit: the archive is analysed
into a report (domains, databases, DB users, crontab, warnings), then a
resumable job maps it to an app, managed databases (MySQL password hashes
preserved where the engine allows) and cron entries, with per-step logs and
retry-from-failed-step. Uploads and fetch-by-URL both supported, with SSRF
and archive-traversal guards throughout.
- **WordPress: import over SSH** — point at any live WordPress site with SSH
credentials (`/wordpress/ssh-import`): probe shows the pinned host key and
site facts, then a job pulls the docroot, dumps the database through the
tunnel and rebuilds it as a managed site with the URL search-replaced.
- **Database tools** — the Database Explorer gains a live processlist with
kill/terminate per server or container; a curated config tuner (RAM-aware
suggested values for vetted MySQL/PostgreSQL settings, applied with backups
and clean rollback, never auto-applied); managed database users tracked as
first-class rows; and one-click "Open in Adminer" SSO via a single-use,
five-minute shadow credential scoped to one database.
- **Per-app resource limits** — CPU and memory limits are first-class fields
on an app (Settings → Resource Limits) showing live usage vs limit, applied
to the container without touching the user's own compose file.
- **One-time login links** — admins can mint single-use, short-TTL,
optionally IP-bound login URLs from Settings → Users, for "log in and take
a look" support situations; links are hashed at rest and reaped hourly.
- **Demo mode** — an opt-in flag that blocks every mutating API call with
`403 demo_mode` and offers seeded read-only credentials on the login page,
for running a public demo of a real panel.
- **Cloud-metadata egress guard** — a default-on (opt-out) firewall rule
blocking app containers from 169.254.169.254, closing the SSRF-to-cloud-IAM
credential-theft class on cloud VPSes (Security → Firewall).
- **Server speed test** — an on-demand download/upload/latency test on the
Monitoring page, using the Ookla/speedtest CLI when present with a
pure-Python fallback.
- **Extensions platform (Phase 7 — settings slot + manifest linting)** — extensions
can now contribute sections to the Settings page (a `settings.section` widget
slot rendered below the active tab), and `plugin.json` manifests are shape-checked
Expand Down Expand Up @@ -268,6 +361,18 @@ awaiting a stable release:

### Changed

- **Marketplace simplified to a two-tab store** — Browse and Installed. The
Installed tab now lists *every* installed extension (built-in, registry,
manual) with the full action set (Update / Configure / Enable–Disable /
Uninstall with keep-vs-purge), replacing the old duplicate "Installed" and
"ServerKit Plugins" tabs. Each row shows where it came from via a
Built-in / Registry / Manual source badge, and registry installs are now
stamped `source_type='registry'` in the database instead of masquerading as
URL installs. Manual install (URL / host folder / zip upload) moved out of
tab-land into an "Install manually" modal off the topbar, and Browse dropped
its KPI strip, sidebar panels, and duplicate category filters in favor of
search + one chips row (plus an All / By ServerKit / Community publisher
filter).
- Overhauled the Docker UI (bulk container stats, compose listing) and migrated
the frontend design system to SCSS `.ui-*` components.
- Unified the local dev launcher (`dev.sh` / `dev.ps1`).
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ English | [Español](docs/README.es.md) | [中文版](docs/README.zh-CN.md) | [P

</details>

<details>
<summary><strong>Marketplace</strong> — Browse and install extensions from the bundled catalog or the remote registry — with a unified Installed tab</summary>

![Marketplace](docs/screenshots/marketplace.png)

</details>

<details>
<summary><strong>Settings</strong> — Profile, security, appearance/branding, users, connections, and notification channels</summary>

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.7.11
1.7.16
69 changes: 69 additions & 0 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def create_app(config_name=None):
from app.middleware.security import register_security_headers
register_security_headers(app)

# Demo mode guard — config-gated (default off), blocks mutating API calls
from app.middleware.demo import init_demo_mode
init_demo_mode(app)

# Register API key authentication middleware
from app.middleware.api_key_auth import register_api_key_auth
register_api_key_auth(app)
Expand Down Expand Up @@ -163,6 +167,14 @@ def create_app(config_name=None):
from app.api.databases import databases_bp
app.register_blueprint(databases_bp, url_prefix='/api/v1/databases')

# Register blueprints - Managed DB users + Adminer SSO
from app.api.managed_db_users import managed_db_users_bp
app.register_blueprint(managed_db_users_bp, url_prefix='/api/v1/managed-databases')

# Register blueprints - Curated DB config tuner
from app.api.db_tuner import db_tuner_bp
app.register_blueprint(db_tuner_bp, url_prefix='/api/v1/db-tuner')

# Register blueprints - Monitoring & Alerts
from app.api.monitoring import monitoring_bp
app.register_blueprint(monitoring_bp, url_prefix='/api/v1/monitoring')
Expand All @@ -179,6 +191,10 @@ def create_app(config_name=None):
from app.api.snapshots import snapshots_bp
app.register_blueprint(snapshots_bp, url_prefix='/api/v1/apps')

# Register blueprints - Declarative serverkit.yaml manifest
from app.api.manifests import manifests_bp
app.register_blueprint(manifests_bp, url_prefix='/api/v1/manifests')

# Register blueprints - Projects & Environments hierarchy
from app.api.projects import projects_bp
app.register_blueprint(projects_bp, url_prefix='/api/v1/projects')
Expand Down Expand Up @@ -366,6 +382,13 @@ def create_app(config_name=None):
# connection stay core (they back /domains); the extension borrows the single
# core CloudflareClient, never a duplicate.

# Register blueprints - DNS provider connections. Core (they back the
# Settings -> Connections DNS tiles and wildcard TLS), but kept at the
# historical /api/v1/email/dns-providers paths from before the email
# extraction so existing frontends keep working.
from app.api.dns_providers import dns_providers_bp
app.register_blueprint(dns_providers_bp, url_prefix='/api/v1/email')

# Register blueprints - Dynamic DNS
from app.api.ddns import ddns_bp
app.register_blueprint(ddns_bp, url_prefix='/api/v1/ddns')
Expand Down Expand Up @@ -450,6 +473,30 @@ def create_app(config_name=None):
from app.api.ai import ai_bp
app.register_blueprint(ai_bp, url_prefix='/api/v1/ai')

# Register blueprints - Server speed test (Monitoring card)
from app.api.speed_test import speedtest_bp
app.register_blueprint(speedtest_bp, url_prefix='/api/v1/speedtest')

# Register blueprints - Site imports (panel migration pipeline)
from app.api.site_imports import site_imports_bp
app.register_blueprint(site_imports_bp, url_prefix='/api/v1/imports')

# Register blueprints - Drift detection + doctor sweep
from app.api.doctor import doctor_bp
app.register_blueprint(doctor_bp, url_prefix='/api/v1/doctor')

# Register blueprints - Diagnostic support bundle
from app.api.support_bundle import support_bundle_bp
app.register_blueprint(support_bundle_bp, url_prefix='/api/v1/support-bundle')

# Register blueprints - Per-site bandwidth accounting
from app.api.bandwidth import bandwidth_bp
app.register_blueprint(bandwidth_bp, url_prefix='/api/v1/bandwidth')

# Register blueprints - .htaccess -> nginx converter (apps-prefixed tool)
from app.api.htaccess_tools import htaccess_tools_bp
app.register_blueprint(htaccess_tools_bp, url_prefix='/api/v1/apps')

# Handle database migrations (Alembic) — must run before plugin loader
# since the loader queries the installed_plugins table.
with app.app_context():
Expand Down Expand Up @@ -566,6 +613,28 @@ def create_app(config_name=None):
ServerOnboardingService.register_jobs()
from app.services.preview_service import PreviewService
PreviewService.register_jobs()
from app.services.metadata_guard_service import MetadataGuardService
MetadataGuardService.register_jobs()
if not app.config.get('TESTING'):
MetadataGuardService.ensure() # converge the metadata egress rule (no-op when unsupported)
from app.services.speed_test_service import SpeedTestService
SpeedTestService.register_jobs()
from app.services import login_link_service
login_link_service.register_jobs()
from app.services.db_admin_sso_service import DbAdminSsoService
DbAdminSsoService.register_jobs()
from app.services.site_import_service import SiteImportService
SiteImportService.register_jobs()
from app.services.drift_service import DriftService
DriftService.register_jobs()
from app.services.doctor_service import DoctorService
DoctorService.register_jobs()
from app.services.file_integrity_service import FileIntegrityService
FileIntegrityService.register_jobs()
from app.services.malware_scan_service import MalwareScanService
MalwareScanService.register_jobs()
from app.services.bandwidth_service import BandwidthService
BandwidthService.register_jobs()
start_job_system(app, seed=seed_builtin_schedules)

# Request body size limit
Expand Down
Loading