Commit Graph
1004 Commits
Author SHA1 Message Date
Juan Denis 3f772c57f6 Add ServerPicker, CopyField, PortField and EnvEditor
One component per job, so pages stop hand-building them:
- ServerPicker: which machine; local first, capacity, capability filter
- CopyField: any value meant to be copied; secrets stay masked
- PortField: host ports checked live against /ports/check, naming the
  app that holds a taken port and offering the next free one
- EnvEditor: KEY=value rows for forms; a pasted .env fills the rows
Call sites move onto them in the following commits.
2026-10-03 22:50:21 -04:00
Juan Denis 30a3bfda82 Add DomainField, one input for picking a domain
Every screen that asked for a domain had its own input: a copied
regex in service settings, a separate "give it a subdomain" modal, a
bare required field on the Domains page. DomainField offers a managed
subdomain (label + base domain) or the user's own domain, normalizes
what is pasted, and says what will happen with DNS before submit.
attachDomain() sends either kind to the right endpoint.

Service settings now has one add-domain flow instead of an attach
form plus a subdomain modal, and the Domains page add modal uses the
same field.
2026-10-03 21:16:02 -04:00
Juan Denis 3e7aedbf17 Move extension, engine and server-template cards onto CatalogCard
The three remaining catalogs each had their own card: a 140px cover
banner with source, category, first-party and trust badges on
Extensions, a legacy SharedCard on Server Templates, and a bespoke
button card in the engine drawer. All three now use the shared
CatalogCard: one tag, author and trust as plain text, an outline
action, and a flat surface. Server-template facts also get singular
forms ("1 package", not "1 packages").
2026-10-03 21:12:32 -04:00
Juan Denis 29dc925e6e Add CatalogCard and move Templates onto it
Extensions, templates, server templates and database engines each
drew their own card with their own badges. CatalogCard is the one
shape for browsable things: icon, name, version line, one plain-text
tag, a two-line description, facts and one action. Templates is the
first user; the icon-only website/docs markers and the hover-reveal
deploy button went with the old card.
2026-10-03 21:08:14 -04:00
Juan Denis e691a0842e Lower the logical-property ceiling to 438 2026-10-03 21:06:05 -04:00
Juan Denis 2b1028f29a Put the Databases header on the shared top bar
The explorer drew its own toolbar with an h1 titled differently from
the sidebar entry and a hand-rolled New menu. It now uses PageLayout
like the other workbench pages, with the shared button and dropdown
primitives; the tree and workspace below are unchanged.
2026-10-03 21:04:15 -04:00
Juan Denis 5930ed5ac4 Put Docker on the shared page shell
Docker was the one list page with its own layout: an inner sidebar
for Containers/Images/Volumes, an Inventory panel repeating the KPI
counts, and a bespoke workbar title. It now uses PageLayout like every
other standalone page, with those sections as top-bar tabs and the
target picker, prune and create actions in the bar. /docker redirects
to /docker/containers so the first tab lights up. The old sidebar,
workbar and container-card CSS was unused and is removed.
2026-10-03 21:02:19 -04:00
Juan Denis 595a686981 Cut filler subtitles and the Marketplace kicker
Section headings in Settings, the new-service wizard, Agent Fleet and
restore points each had a one-line description that restated the
heading. The Installed section header said "installed" three times.
Only lines that say something specific (what disabling a module does,
install instructions, user data) are kept.
2026-10-03 20:54:39 -04:00
Juan Denis c7ea5ef3db Drop the servers hex-colour baseline to zero 2026-10-03 20:54:35 -04:00
Juan Denis d4aaede728 Flatten gradient surfaces, drop dead servers CSS
Detail headers and table heads used a top-lit gradient plus a radial
accent wash. They are now a flat surface, matching every other card.
The old Servers card layout (hero, health panel, card grid, empty
state) was no longer referenced anywhere and carried most of the
remaining gradients, so it goes.
2026-10-03 20:51:29 -04:00
Juan Denis 4c70658f90 Harden the Cloud tunnel per review
Session keys must name their purpose, and both loopback legs skip
environment proxies so the grant and bearer token never leave the box.
In the tunnel the API always goes through the relay, which attaches the
grant, and the two raw root anchors become router Links so they stay
under the /t/<device> prefix.
2026-10-01 22:47:48 -04:00
Juan Denis 39024497ee Bump js-yaml and brace-expansion in frontend
js-yaml override 4.3.1 -> 4.3.2 and brace-expansion 1.1.18 ->
1.1.21 clear the merge-key CPU and brace-rewrite ReDoS alerts.
2026-10-01 22:04:09 -04:00
Juan Denis c7b6f145d1 Serve the panel through the Cloud relay tunnel
Until now the connect client refused every http and sio stream, so
Open panel in ServerKit Cloud showed a blank page. The client now
bridges them to the loopback listener, the panel serves its page
under the relay's /t/<device>/ prefix, and the SPA prefixes its
router, API and Socket.IO paths from it. The Cloud grant is checked
against Cloud's JWKS before signing in the panel user with the same
email; panel two-factor still applies.
2026-09-29 23:19:34 -04:00
Juan Denis 5358ba42ab Render every Marketplace cover image at one size
Registry logos drew at 44px beside 92px bundled icons, and the white
tile was picked by whether bundled art existed rather than by what
rendered, so a registry-only logo like Minecraft's sat on the gradient
meant for white glyphs. The cover now picks its tile from the image it
actually shows, and a failed logo drops back to the gradient.
2026-09-29 20:08:06 -04:00
Juan Denis 79ba873663 Stop map callbacks shadowing the translator t
TagsPanel and RemoteAccess named their .map item t, so every t('...')
inside called a tag or tunnel object and crashed the page as soon as a
tag or tunnel existed.
2026-09-29 19:52:15 -04:00
Juan Denis d0b11714e7 Deploy compose apps through A/B slots
Plan 87 phase C. A compose app on slots runs each slot as its own
project, <name>-a / <name>-b, rendered from the merged compose config:
container_name stripped, only the web service published, on the
slot's loopback port, and every named volume pinned to the name it has
today. Without the pin compose prefixes volumes with the project name
and slot B would start on empty ones; the real-Docker leg checks this.

Stateful services (a database, cache or queue with a named volume)
cannot run twice. They move once, on an explicit previewed split, into
a shared <name>-data project that both slots reach by the same names.

The webhook, template-update and rollback paths deploy through the
engine instead of stopping the stack. The --scale web=2 rolling
restart and the Webhook.zero_downtime flag are deleted: it assumed a
service named web, ignored its own health result, and no UI set it.
Compose slot images get immutable per-deployment tags and are pruned
with the rest.
2026-09-28 22:26:44 -04:00
Juan Denis 0749508599 Run release commands and DB snapshots before the switch
Plan 87 phase D. Both slots share the database, so what changes it
runs once, before the switch, while the old release still serves:
the app's managed databases are dumped (a warning, never a blocker),
then the release command runs in a one-off container of the new
image. A failed release aborts the deploy with the live slot
untouched. The command comes from the app's setting, the Procfile
release: line, or serverkit.yaml; a rollback never re-runs it.

Rolling code back never rolls the database back: after a switch
back the slot card offers the pre-deploy snapshot as an explicit
second step with a data-loss warning. stop_old_before_release is
the announced-downtime fallback for schema changes that cannot stay
backward-compatible for one deploy.
2026-09-28 21:41:52 -04:00
Juan Denis c697e7247b Show slot deploys in the console and app detail
Plan 87 phase F. A slot deploy job lists its real stages (Preflight,
Build, Boot slot X, Health gate, Switch, Watch) and advances through
them as the engine reaches each one, so PipelineStrip renders them
unchanged. The overview gets a slot card: which release is live,
which is the standby and how long it stays warm, with Switch back;
when slots are off it lists why the app does or does not qualify.
Settings gains the opt-in, watch window, standby minutes and the
shared-volume confirmation.
2026-09-28 21:33:45 -04:00
Juan Denis cecbb743fd Make redeploys fail safe before slot deploys
Plan 87 phase A. Fixes the deploy bugs that make a rollback or a
health check lie:

- builds get an immutable tag per deployment, so rolling back runs
  the image that actually ran instead of whatever :latest became;
  old tags are pruned so this is not a new disk leak
- the deployment records the commit after the pull, not before
- the health wait is a real gate: a 4xx fails, it takes three passes
  in a row, an exited container fails at once, and it raises
- a per-app deploy lock makes a second deploy wait for the first
- a compose rollback rebuilds and recreates its containers, and a
  rollback with no version finds the previous release again
- a failed template update says when restoring the old stack failed

Health path, timeout and the 4xx rule are editable in Settings.
2026-09-28 21:10:29 -04:00
Juan Denis 58e5096368 Drop client calls to the removed nginx routes
The load-balancer builder's /proxy POST and /lb-methods were deleted
with the scaling pieces, but their API client methods stayed behind;
the route contract test caught them.
2026-09-28 09:28:14 -04:00
Juan Denis acdf6b878d Remove auto-scaling and the load-balancer builder
Replica counts could be changed, but nginx only proxied to one port
and the auto-scale sweep was never scheduled, so neither the
Auto-scale section nor its five routes could work; the nginx
upstream builder had no UI at all. With one live copy per app they
are gone, and the scale-policy table is dropped. Running containers
are not touched. The scaling spec now says replicas are not
pursued.
2026-09-28 09:03:34 -04:00
Juan Denis e3fb1410cd Flag apps that keep crashing
A container with a restart policy that keeps dying looked running.
A one-minute builtin tick counts each app's container restarts;
three within ten minutes opens a crash-loop incident through the
monitor incident path and puts a hint on the Overview, and ten quiet
minutes resolve it. Build-pack workers count too.
2026-09-27 23:05:56 -04:00
Juan Denis e688b7b5c3 Let proxied sites long-cache fingerprinted assets
The Docker vhost set no caching at all. An opt-in rule serves files
whose name carries a content hash with a one-year immutable
Cache-Control, proxying exactly like location /. A name only counts
as hashed with 8+ characters and a digit, so app-settings.js is never
pinned. Off by default: the panel cannot know an app's asset layout.
2026-09-27 22:45:53 -04:00
Juan Denis 3feba262cb Name an app's likely bottleneck on its Overview
A rule-based check reads the app's request metrics, its container
CPU and the CPU and top queries of the databases it references, and
names what fits: page cache for a busy app over an idle database,
a cache or index when one query dominates a busy database, slot
deploys for errors during deploys, and TTL for a low hit ratio. Each
hint links to the fix and states its failure mode; nothing is added
automatically.
2026-09-27 22:36:14 -04:00
Juan Denis 45f7100d93 Offer an opt-in PgBouncer beside PostgreSQL
An installed PostgreSQL can switch on a transaction-mode PgBouncer
sidecar through its managed override, with no host port, on the
shared network. fromService gains pooledUrl; apps keep the direct
connectionString unless they switch, because transaction pooling
breaks prepared statements in some drivers, LISTEN and advisory
locks, and the card says so.
2026-09-27 22:27:32 -04:00
Juan Denis 0c18aa421f Trace apps through an OpenTelemetry Collector
A new collector template receives OTLP; Attach → Tracing gives an
app the standard OTEL_EXPORTER_OTLP_* variables, and a Jaeger
attached to the collector as its trace store receives the spans.
ServerKit does not instrument apps; it only wires the path. A docker
leg sends a span from an app and finds it in Jaeger.
2026-09-27 22:13:55 -04:00
Juan Denis 722072a14b Wire Grafana to Prometheus and Loki by attaching
The Prometheus, Grafana and Loki templates installed but connected
to nothing. A Grafana install now takes Metrics and Logs
attachments: the panel creates the data source through Grafana's
API with the recorded admin login, and a metrics attach also imports
a dashboard of the panel's fleet metrics. An attached app joins the
shared network, so Grafana reaches the source by name.
2026-09-27 21:53:52 -04:00
Juan Denis dc08f6975c Show top queries and hit ratios per database
The explorer showed live sessions but nothing about which queries
cost the most or whether the cache was working. A container's new
Insights tab lists top queries by total time (pg_stat_statements,
performance_schema digests) with connections in use and the buffer
cache hit ratio, and says why a list is empty. PostgreSQL's
pg_stat_statements is switched on through the config tuner, keeping
existing preloads, so its rollback undoes it.
2026-09-27 21:30:07 -04:00
Juan Denis 6986f497f3 Attach a cache, storage or queue from the app page
Installed Redis and a running app were unrelated until someone wrote
the env by hand. The Overview's Attached services card reuses an
installed service or installs one, writes the fromService references
(REDIS_URL, AMQP_URL/BROKER_URL, the S3_* set), shows that kind's
failure mode, and offers a redeploy. Detach removes exactly what the
attach wrote.
2026-09-27 21:16:58 -04:00
Juan Denis 4fdd7e31d6 Run Procfile workers beside a build-pack app
Only the Procfile web: line was read, so worker: and scheduler:
never ran. Each now runs as serverkit-app-<id>-<process> from the
same image with the app's env and volumes, no port, and a restart
policy; start, stop and restart follow the web container, and a line
removed from the Procfile goes on the next deploy. Single-container
apps also join the shared service network, which only compose apps
did, and the app's container list now shows the web and its workers.
2026-09-27 21:03:59 -04:00
Juan Denis d148ccc247 Make the micro-cache a real page cache
Each site now sets its own TTL (10s default, 300s cap), and serves
the last good page while the app errors or the entry refreshes, with
one upstream request per missing entry. The cache key carries the
host, so purge clears only this site's files instead of every site's
cache. The settings panel shows the 24h hit ratio.
2026-09-27 18:51:05 -04:00
Juan Denis d2fd3b38b0 Show per-app request rate, errors and latency
A one-minute builtin tick tails each app's timed access log from a
stored byte offset and rolls it into minute and hour rows: status
classes, cache HIT/MISS/BYPASS/STALE and a latency histogram, so
p50/p95/p99 merge exactly across ticks and into hours. Minute rows
go after 48 h and hour rows after 30 days on the same tick. The
Overview tab gets a Traffic card.
2026-09-27 18:18:30 -04:00
Juan Denis 7c303f856c Add a Storage tab to admin settings
Shows where the panel host's disk goes, when the rollback install is
removed, and the retention windows that bound ServerKit's own share,
with cleanup handed to the existing reclaim modal.
2026-09-27 16:40:48 -04:00
Peter Dave Hello ad97c9f658 Update and improve Traditional Chinese locale 2026-09-27 22:49:18 +08:00
Juan Denis 45bc3c70bd Guard first admin with a setup code, fix Git deploys
A fresh panel no longer lets whoever reaches it first become admin:
registering the first account needs a one-time code the installer
prints (`serverkit setup-code` repeats it). SERVERKIT_ADMIN_EMAIL and
SERVERKIT_ADMIN_PASSWORD create the admin unattended instead.

Git apps now deploy what users expect: a repository with a compose
file deploys every service it declares, "Deploy latest" builds the
branch's newest commit, and push webhooks rebuild the containers.
Also fixes branch switching on single-branch clones, uploads of a
lone file, compose v1 port preflight, and CLI commands that ignored
their flags.
2026-09-24 00:50:02 -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
Juan Denis fc5a041764 Fix first-run sidebar preferences and extension loading [skip version] 2026-09-11 00:17:39 -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 4dc281ce58 test(settings): await both mobile footer actions [skip version] 2026-09-08 09:40:06 -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 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 5a4036e9b2 fix: resolve credential draft and backup fallback review [skip version] 2026-09-08 01:39:54 -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 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 ef0f9481af Update changelog for PRs #136/#137/#139 and fix hook dep warnings 2026-09-07 20:08:29 -04:00
Juan Denisandjrbertt 4f361fa3d1 Merge PR #136: fix(observability): resolve container runtime logs and metrics
Co-authored-by: jrbertt <joaorebertt0219@gmail.com>
2026-09-07 20:08:16 -04:00
Juan Denis d88f1cbf55 Make service detail chips scroll on mobile
Updates the service detail status chip layout for small screens from wrapped columns to a single horizontal, non-wrapping scroll row. It flattens group wrappers, hides group labels on phones, and prevents chip/ring clipping with small spacing and padding adjustments so pills remain readable and tappable.
2026-09-07 19:51:22 -04:00