Commit Graph
1290 Commits
Author SHA1 Message Date
Juan Denis b7911026be Merge plan-47 lean-core (wizard installs + 4 backend extractions + security tab-group) 2026-07-15 15:24:07 -04:00
github-actions[bot] 514873f230 chore: bump version to 1.7.37 [skip ci] 2026-07-15 18:20:14 +00:00
Juan Denis ec35690582 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-15 14:19:25 -04:00
Juan DenisandClaude Opus 4.8 56c1ab6a27 fix(favicon): persist the per-install tint + apply it on update too
Two gaps for existing installs:

- The tint ran only in install.sh, so `serverkit update` (which has its own
  build path in scripts/update.sh) would rebuild with the DEFAULT color and a
  fixed favicon.ico — existing installs would never get a unique mark.
- Re-tinting on every rebuild would also repaint the box on each update.

Fix: assign the color ONCE and persist it in /etc/serverkit/favicon-color
(survives updates), then re-apply the SAME color from both install and update.
update.sh applies it to the staged dist unconditionally (after the guarded
build), so it works whether dist was rebuilt (source mode) or shipped prebuilt
(release). An install that predates the feature picks up a color on its first
update and keeps it thereafter — a car keeps its paint.

Verified: bash -n on both scripts; persistence confirmed (re-run reuses the
stored color).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:08:26 -04:00
Juan DenisandClaude Opus 4.8 8ee33ef2c6 fix(favicon): make the per-install tint actually cover the fingerprint URLs
The install-time tint only recolored favicon.svg, but the page also shipped a
fixed raster favicon.ico + .png — and favicon-hash scanners hash /favicon.ico,
which was identical across every install. So the "car-paint" tint didn't change
what those tools cluster on.

- index.html links only the SVG now (drop the raster <link>s).
- Remove the unused favicon-16x16/32x32.png.
- install.sh: after tinting favicon.svg, copy it over favicon.ico too, so a
  blind /favicon.ico fetch also returns the per-install color instead of a fixed
  icon. (SVG bytes under an .ico name — browsers use the linked SVG; this only
  affects what a scanner hashes at that URL.)

Still obscurity/defense-in-depth, not a real gate — but now it does what it says.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:04:00 -04:00
Juan DenisandClaude Opus 4.8 3954680c87 fix: apply login layout to the main login form + bind BYO-image legacy port to loopback
Two issues from PR review:

- Login: the AuthLayout wrapper only reached the 2FA and login-link states —
  the primary login form still had a hardcoded auth-container/auth-card, so the
  `login_layout` setting didn't affect the page most users see. Wrap the main
  form in AuthLayout too.
- apps.py: the legacy scalar-port fallback for a BYO-image app omitted `expose`,
  which AppPortService defaults to `public` (binds 0.0.0.0), publishing the port
  past nginx. Default it to `local` (loopback). (Workers set no port, so this
  was latent, but the generic path is now safe.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:55:15 -04:00
github-actions[bot] d7af18a7a9 chore: bump version to 1.7.36 [skip ci] 2026-07-15 17:19:38 +00:00
Juan DenisandClaude Opus 4.8 be0882ca24 chore(frontend): reconcile package-lock with package.json
The lockfile had drifted from package.json (which is why `tramo` — a declared
dep — was never installed and `vite build` failed to resolve `tramo/react`).
`npm install` reconciled node_modules; commit the resulting lockfile so `npm ci`
installs are reproducible and the frontend builds cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:17:25 -04:00
Juan DenisandClaude Opus 4.8 45b6f8bc52 refine: per-install favicon uses a continuous shade, not a fixed palette
A fixed list of N colors means two installs collide on the identical hex. Use
the "car-paint" model instead: a curated base hue (tasteful families, no muddy
yellows) + a few degrees of jitter + randomized saturation/lightness (rich,
mid-dark — never washed-out). Same family can recur, but the exact shade rarely
does, so favicon-hash comparison across installs almost always differs. Emitted
as an hsl() fill (SVG renders it natively; still a pure text replace).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:14:07 -04:00
Juan DenisandClaude Opus 4.8 882b7bb8e2 feat: brand-neutral public title + per-install favicon tint
Reduce how identifiable a panel is from its unauthenticated surface, without
losing the brand once you're inside.

- Public vs interior title: new `public_title` setting (default "Control
  Panel") drives the sign-in/register/setup headings and the browser tab
  before login (and the static first-paint <title>); `panel_title`
  (ServerKit) still names the tab once signed in. PageTitleUpdater picks by
  route. Both are exposed pre-auth on /auth/setup-status and editable in the
  admin Site Settings → Panel Appearance card.
- Per-install favicon tint (install.sh randomize_favicon): the favicon is a
  plain-text SVG, so each install recolors the tile to one of a curated set of
  legible colors via a string replace — same mark, different color, so a
  favicon-hash comparison across installs differs. No image tooling required;
  if the step can't run, the shipped default color stays. Runs on both fresh
  and release installs; existing installs are untouched.

Defense-in-depth / obscurity only — real protection is access control + auth.
Verified: eslint 0 errors, backend py_compile ok, install.sh `bash -n` ok,
favicon sed recolor confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:12:23 -04:00
Juan DenisandClaude Opus 4.8 9a1a1f69a3 feat: configurable panel title + switchable login layouts
Make the panel's identity admin-configurable instead of hardcoded, and let
operators pick a login-page layout — so instances can be branded/varied from
the panel rather than by editing source.

- Settings: new `panel_title` (default "ServerKit") and `login_layout`
  (default "centered") keys via SettingsService (key/value — no migration).
- Exposed pre-auth on the existing GET /auth/setup-status so the login page
  reads them before any authenticated call.
- Frontend threads panelTitle + loginLayout through AuthContext; document.title
  (App.jsx PageTitleUpdater + usePageTitle) and the auth-page headings use the
  configured title, with import.meta.env.VITE_PANEL_TITLE as the build-time
  default for the first paint.
- Login page refactored to an AuthLayout selector with three variants —
  Centered (current), Split-hero, Minimal — chosen by `login_layout`.
- Admin UI: a "Panel Appearance" card (title field + layout dropdown) in the
  admin-only Site Settings tab; saves via the generic settings API and calls
  refreshSetupStatus so the running SPA updates live. Searchable via a new
  settingsIndex entry.

Register/Setup adopt the configured title on their headings. manifest.json
stays static (documented limitation). Verified: eslint clean on all touched
files (0 errors), settings-index guard passes. (The repo's full `vite build`
currently fails on an unrelated pre-existing plugin import, serverkit-tramo →
`tramo/react`, independent of this change.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:01:39 -04:00
Juan DenisandClaude Opus 4.8 70027b9fea Revert panel de-branding — keep the ServerKit identity, keep only noindex
Restore the ServerKit name, favicon, manifest, auth-page headings, and
service-worker strings. Genericizing the panel's identity was the wrong
trade: hiding the title/favicon is security-by-obscurity that a real scanner
sees through, and it erased the product identity for almost no benefit. The
pages it touched (login/setup/register) require server or admin access to
reach anyway, so there is nothing to hide there.

The one durable, no-cost measure is kept: the `X-Robots-Tag: noindex`
header (from the prior commit) stays on both nginx server blocks so the
panel isn't indexed by search engines. Real protection lives in access
control + auth, not in the favicon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:40:21 -04:00
Juan DenisandClaude Opus 4.8 7c38e44ba0 chore: drop orphaned old favicon assets under src/assets/favicon
These raster favicons (favicon.ico/.png) were unreferenced dead files — the
app only ever served /favicon.* from public/. Removing them completes the
favicon cleanup from the de-fingerprint change; the served icon is the
generic public/favicon.svg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:36:29 -04:00
Juan DenisandClaude Opus 4.8 70d445da32 harden: de-fingerprint the panel + keep it out of search indexes
Reduce how easily the panel can be enumerated by scanners (Shodan/Censys)
and search engines:

- nginx: emit `X-Robots-Tag: noindex, nofollow, noarchive` on the panel
  server blocks (both the TLS and insecure fallback configs).
- Generic page identity: title "Control Panel" + neutral meta description
  in index.html; the runtime document.title setters (App.jsx, usePageTitle)
  and PWA manifest name/short_name match — so the browser tab and manifest
  no longer say "ServerKit".
- Drop the distinctive favicon (.ico/.png removed, generic favicon.svg) so a
  favicon-hash no longer clusters instances; strip the product name from the
  service worker (cache name, push title, header comment).
- Neutralize the visible auth/splash copy (Login/Register/Setup headings,
  AppLoader label) to a generic name.

Search engines are the easy case; the header is defense-in-depth. Real
hiding still requires access control (IP allowlist / VPN / zero-trust) and
is deployment-specific.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:32:15 -04:00
Juan DenisandClaude Opus 4.8 69123a8b80 harden: broaden insecure-secret detection, bind API to loopback, fix break-glass sub
- config: is_insecure_secret() rejects any dev/placeholder key (dev-*,
  jwt-secret-key*, *change-in-production*, *change-this*, empty), not just the
  two exact default strings — a variant like `dev-jwt-...` can no longer boot
  in production. Used in both ProductionConfig.__init__ and init_app.
- systemd unit: bind gunicorn to 127.0.0.1 by default so the raw API port is
  not world-reachable (host nginx already fronts :80/:443). Override with
  SERVERKIT_BIND_HOST=0.0.0.0 for setups that front it differently.
- cli break-glass: mint the token with a string identity; flask-jwt-extended
  rejects a non-string `sub` at decode time with a 422, which broke every CLI
  call that authenticated against the panel API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:07:14 -04:00
Juan DenisandClaude Opus 4.8 1428aa8f29 feat(apps): start local image apps that have no source on disk
A docker app created with a `docker_image` but no repository (root_path /
compose_file NULL — e.g. an `image:` manifest service with no git source)
previously skipped the launch branch in start_app entirely and was just
marked `running`, so nothing ever started.

On first start, materialize a one-service compose from the image and its
typed ports under APPS_DIR, persist root_path/compose_file, and let the
normal compose_up overlay inject the app's effective env (including
resolved vault secrets) — so nothing sensitive is written to the base
compose. No-op when a source already exists, there is no image, or the
app targets a remote server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 11:50:55 -04:00
Juan DenisandClaude Opus 4.8 176141295f docs(plan47): sync extension manifests, raise ratchet, changelog + docs
Plan 47 Phase 6 (for shipped work). Sync the pre-bundled frontend plugin.json
copies for the four extracted builtins (entry_point/url_prefix + status
description) so builtin-extensions/ and frontend/src/plugins/ agree
(sync-builtin-frontends --check + test_extension_manifests green). Raise the
test-count ratchet 2588 -> 2623. Document the second-speed backend-extraction
recipe in EXTENSIONS.md, add CHANGELOG entries (wizard installs + opt-in
FTP/cloud/remote-access/status), and a README "lean by default" bullet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 23:05:06 -04:00
Juan DenisandClaude Opus 4.8 a5e19ce6bf feat(security): open the Security tab group to extension contributions
Plan 47 Phase 3a. SECURITY_TABS' TabGroupLayout now passes groupId="security"
and mounts {groupRoutes.security}, so extracted security features (fail2ban,
clamav, …) can contribute tabs + group-nested routes via their manifests and
return as tabs in the exact place users know — the same mechanism the
files/servers/monitoring groups already use. Inert until a security extension
ships; no behavior change on its own.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:38:35 -04:00
Juan DenisandClaude Opus 4.8 95681f8b80 refactor(status): extract status-pages backend into serverkit-status extension
Plan 47 Phase 2. The status-pages blueprint (public /status/<slug> + /badge +
authenticated management API) and its service move into
builtin-extensions/serverkit-status/backend/ (entry_point status_pages:
status_pages_bp, url_prefix unchanged). Core no longer registers /api/v1/status
(management) or imports the service; the unused /api/v1/observability/status-pages
alias is dropped. The StatusPage/StatusComponent/etc models stay core (G2). The
public status/badge pages work whenever the extension is installed.

The core WordPress health-check job (run_health_checks) now reaches
StatusPageService.sync_component_from_health via get_installed_extension_attr and
no-ops cleanly on a lean panel.

Tests: test_status_extraction.py (8) — extraction shape, install=routes,
health-job no-op, relocated status-page mutation + component-check authz.
Adjusted test_observability_namespace + test_api_authz_hardening accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:34:24 -04:00
Juan DenisandClaude Opus 4.8 e6bf4915b4 refactor(remote-access): extract WireGuard-tunnels backend into extension
Plan 47 Phase 2. The tunnels blueprint + broker/publish/netutil services move
into builtin-extensions/serverkit-remote-access/backend/ (entry_point tunnels:
tunnels_bp, url_prefix unchanged). Core no longer registers /api/v1/tunnels or
imports the services; the Tunnel/ExposedService models stay core (G2).

New reusable core->extension seam get_installed_extension_attr(slug, module,
attr): the agent gateway reaches TunnelBrokerService.schedule_reconcile through
it, so tunnel self-heal still fires when the extension is installed and no-ops
cleanly otherwise. Backend re-acquisition on upgrade is covered by the Phase-2
migration.

Tests: test_tunnel_netutil.py + test_tunnel_reconcile.py re-homed into
test_remote_access_extraction.py (17), which installs the extension and runs the
helpers/reconcile against its modules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:27:02 -04:00
Juan DenisandClaude Opus 4.8 44a73d7796 refactor(cloud): extract cloud-provision backend + add upgrade re-acquisition
Plan 47 Phase 2. The cloud-provider blueprint + service move out of core into
builtin-extensions/serverkit-cloud-provision/backend/ (entry_point, url_prefix
unchanged). Core no longer registers /api/v1/cloud or imports the service; the
CloudProvider/CloudServer models stay core (G2). The one-time legacy cloud-secret
encryption is dropped from core boot (already run on any panel reaching this
version; the extension encrypts on write).

Also adds run_backend_acquisition() — a one-shot migration that re-acquires the
now-extracted backend on upgraded panels that installed a converted builtin
frontend-only (whose API used to come from core). Without it, ftp/cloud/etc would
lose their API on upgrade. Wired into create_app after run_auto_install.

Tests: test_cloud_provision_extraction.py (7, incl. relocated secret-at-rest +
backend-acquisition migration); secret-encryption cloud block moved out of core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:22:18 -04:00
Juan DenisandClaude Opus 4.8 2a060c518d refactor(ftp): extract FTP backend into serverkit-ftp builtin extension
Plan 47 Phase 2. The FTP blueprint + service move out of core into
builtin-extensions/serverkit-ftp/backend/ (entry_point ftp:ftp_bp, url_prefix
unchanged). Core no longer registers /api/v1/ftp or imports ftp_service — a
fresh panel that never touches FTP loads none of it. Already in
CONVERTED_BUILTIN_SLUGS, so upgrades re-acquire the backend automatically.

Acceptance (tests/test_ftp_extraction.py): uninstalled = no /api/v1/ftp routes +
app.services.ftp_service gone; installed = blueprint hot-loads and /ftp/status
responds; uninstall removes it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:15:07 -04:00
Juan DenisandClaude Opus 4.8 f58bdb6823 feat(setup): wizard installs recommended extensions; lean-by-default flagships
Plan 47 Phase 1. The onboarding "Recommended for you" step now installs real
extensions matched to the selected use cases instead of rendering decorative
strings:

- Backend use-case -> slug map + resolver (RECOMMENDED_EXTENSIONS_BY_USE_CASE,
  recommend_extensions_for_use_cases) single-sourced and test-guarded against
  dead slugs; exposed via GET /api/v1/plugins/recommendations.
- SetupStepSummary renders default-checked install checkboxes with each
  extension's real display_name/description and installs source-aware on Finish
  (per-item progress, fail-soft: an install failure never blocks setup).
- complete-onboarding persists onboarding_installed_extensions.
- WordPress flagship seeding is now gated on needs_setup(): fresh installs skip
  it (the wizard installs it when the WordPress use case is picked); completed
  -setup/upgraded installs keep it (zero regression). TESTING bypass keeps the
  WordPress suite green; the real gate is covered by dedicated proving tests.

Proving tests: tests/test_onboarding_recommendations.py (12).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:11:59 -04:00
github-actions[bot] b2ada5bcae chore: bump version to 1.7.35 [skip ci] 2026-07-13 01:59:49 +00:00
Juan DenisandClaude Fable 5 783b0876bd Build the frontend stage natively in multi-arch Docker builds
The release workflow builds linux/amd64,linux/arm64 via buildx + QEMU on
an amd64 runner, so the frontend-builder stage ran `npm ci`/`npm run
build` under emulation for arm64 -- 20-40 minutes when it did not hang
outright. Its output (frontend/dist) is static, arch-independent assets,
so pin the stage to $BUILDPLATFORM: buildx runs it natively, once, shared
by both platform images. Final images are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 21:59:37 -04:00
github-actions[bot] 09f3a2312d chore: bump version to 1.7.34 [skip ci] 2026-07-13 01:42:36 +00:00
Juan DenisandClaude Fable 5 0c8c578121 Sweep retired extension leftovers at boot
Backstop for the updater-side skip: remove_retired_extensions() deletes a
retired extension''s copy-installed files and InstalledPlugin row on every
boot, BEFORE the plugin loader, so it is never loaded, never repaired
back, and never present for an older/offline updater to carry forward
into the next frontend build. Also drops serverkit-workflows from
CONVERTED_BUILTIN_SLUGS -- it no longer ships, so the auto-install
candidate was a permanent no-op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 21:42:26 -04:00
Juan DenisandClaude Fable 5 7055e3b498 Never carry retired plugins into an update; quarantine build-breakers
The 1.7.25 -> 1.7.33 update broke on panels that still had the retired
serverkit-workflows extension on disk: preserve_installed_plugins carried
it into the fresh tree, where its reactflow import (removed with plan 45)
sank the whole bundle build and blocked the update.

- RETIRED_PLUGIN_SLUGS: the carry-forward now skips slugs the product
  deliberately deleted, and docker in-place mode strips their leftovers
  after the hard reset (untracked dirs survive it).
- Quarantine-and-retry: when the bundle build fails and plugins were
  carried forward, move them into /var/backups/serverkit/quarantined-
  plugins-<ts> and retry once -- fail the plugin, never the update.

The updater self-refreshes from main before running, so this protects
every panel on its next `serverkit update` once merged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 21:42:16 -04:00
github-actions[bot] 5e13aee826 chore: bump version to 1.7.33 [skip ci] 2026-07-12 10:14:55 +00:00
Juan DenisandClaude Opus 4.8 f909103656 Add per-row busy state to Marketplace plugin actions
Track the in-flight plugin id/action so uninstall/update/enable/disable can't be double-fired and each row shows live progress; await loadExtensions() before clearing busy. Includes matching tramo editor and workflow_store tweaks (source + live copies) and tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 06:14:45 -04:00
Juan DenisandClaude Opus 4.8 89af823b39 docs: use new poster as README banner and update tagline
Replace the logo image at the top of the English and translated READMEs with the new docs/screenshots/poster.png banner, and change the tagline to "Deploy, manage, and monitor servers." (translated per language).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 06:14:37 -04:00
github-actions[bot] 10f7c71b93 chore: bump version to 1.7.32 [skip ci] 2026-07-12 08:16:17 +00:00
Juan Denis 6bae6a7a1c Align K8s page with tabgroup layout
Updated both K8sPage copies to use the full-bleed `sk-tabgroup` page shell and content wrappers, including the loading state. This moves warning/empty/main tab content into `sk-tabgroup__content`/`__inner` for consistent spacing and tabbed-page structure, and removes the extra topbar meta text to match the new header pattern.
2026-07-12 04:16:15 -04:00
Juan DenisandClaude Opus 4.8 128143760d chore: sync builtin-extension frontend artifacts (fix CI drift)
Regenerate frontend/src/plugins/<slug>/plugin.json from source via
scripts/sync-builtin-frontends.mjs. Resolves the drift check: k8s was a
missing trailing newline; tramo/wordpress/cloudflare-ops/gpu are the new
featured/feature_score fields normalized to canonical form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 03:16:28 -04:00
github-actions[bot] feacf23349 chore: bump version to 1.7.31 [skip ci] 2026-07-12 07:07:10 +00:00
Juan Denis ff8721d58e Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-07-12 03:06:32 -04:00
Juan Denis 053d987721 Add Kubernetes plugin backend and manifest entry
Introduces the new `serverkit-k8s` backend plugin with full extension wiring: plugin metadata, model registration, lifecycle hooks, kubectl execution service, cluster CRUD/default handling, connectivity checks, normalized read endpoints (nodes/namespaces/workloads/pods/services/ingresses/logs/overview), and admin mutations (scale/restart/delete/apply). It also registers the plugin in the frontend plugins manifest (including plugin styles) so the Kubernetes page can be surfaced by the plugin system.
2026-07-12 03:06:16 -04:00
Juan DenisandClaude Opus 4.8 4fa318fa8b feat(marketplace): curated featured top-5 + k8s/wordpress covers
- Featured order: Faro (100), Automations (90), WordPress (80),
  Cloudflare Zone Ops (70), GPU Monitor (60). Scores set in each bundled
  extension's manifest (both copies); Faro's lives in the registry index.
- Add illustrated covers for Kubernetes and WordPress (extension-icons +
  resolver rules) so they stop falling back to generic category glyphs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 03:01:33 -04:00
Juan DenisandClaude Opus 4.8 76a2788d24 refactor(ui): rename Marketplace to Extensions
"Marketplace" reads as a store; the page is where you browse and install
extensions, so call it Extensions.

- Nav label + page/tab title + route /marketplace -> /extensions
  (first tab drives the topbar title via TabGroupLayout).
- /marketplace and /marketplace/installed now redirect to /extensions.
- Update command palette, page-context map, NotFound, empty-state copy.
- Keep the internal nav id, CSS classes, component/file names, and the
  backend /api/v1/marketplace/registry endpoints unchanged; palette keeps
  "marketplace" as a search keyword so the old term still finds it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:37:17 -04:00
Juan DenisandClaude Opus 4.8 161271dc5d fix(marketplace): surface registry featured flag; distinct mail icons
- registry_service: keep featured/feature_score through normalization so
  registry (bundled:false) extensions like Faro actually float to the top
  and show the Featured ribbon (they were being stripped).
- ExtensionIcons: give the Postfix/Dovecot "Email Server" the envelope and
  the Stalwart "Mail Server" the server-rack icon so they stop looking like
  duplicates ('email' matched before the bare 'mail' rule).
- Drop Faro's logo override so it uses its full-bleed illustrated cover
  (92px) instead of the 44px brand-logo slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:27:57 -04:00
Juan DenisandClaude Opus 4.8 050ae99390 chore(marketplace): bump Faro fallback entry to v1.0.2
Match the published serverkit-faro v1.0.2 release (lean zip) in the
offline registry fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:19:12 -04:00
Juan DenisandClaude Opus 4.8 30fbdb3bf8 feat(marketplace): feature Faro + Cloudflare Zone Ops, add Faro cover
- Add Faro illustrated cover (extension-icons/faro.png) + resolver rule so
  serverkit-faro shows a 3D lighthouse instead of a flat glyph.
- Mark Cloudflare Zone Ops featured (score 95) in both plugin.json copies.
- Add a wordpress.detail.panel PluginSlot so the Faro (and future WP)
  extensions can render on WordPress site pages.
- Sync the registry offline fallback with the Faro v1.0.1 entry (featured).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:09:31 -04:00
github-actions[bot] 1384a3e5bb chore: bump version to 1.7.30 [skip ci] 2026-07-12 04:27:53 +00:00
Juan DenisandClaude Opus 4.8 0ed76e1d7c feat(tramo): add serverkit-tramo Automations plugin backend
Node-based automation builder powered by the tramo engine, embedded as an
opt-in builtin plugin (Automations, /automations). Panel is system of record
for workflow docs; a managed @tramo/server Docker container on the host runs
them headless with run history, approvals, and inbound webhooks proxied
through the panel. Replaces the older Workflow Builder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:27:41 -04:00
Juan DenisandClaude Opus 4.8 475c492d19 feat(marketplace): featured Automations with illustrated icon
- Add an illustrated 3D cover icon for the Automations extension
  (assets/extension-icons/automations.png, transparent bg, 256px) and map
  tramo/automation to it in the extension icon resolver.
- Add manifest 'featured' + 'feature_score' (Automations = featured, score 100).
- Sort featured extensions to the top of the Marketplace by score (score is
  hidden; used only for ordering) and show a gold 'Featured' ribbon on the
  cover + a subtle featured border.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:21:42 -04:00
Juan DenisandClaude Opus 4.8 483f1fa4c0 feat(automations): side-nav Settings layout (like core/WordPress settings)
Replace the single long scroll of stacked cards with the core settings-layout:
a left sub-nav (Engine / Events & port / Pack secrets) and a content pane that
shows only the selected section. Reuses the global .settings-layout/.settings-nav
classes so it matches core Settings and WordPress. Drops the redundant per-card
Refresh (the topbar Refresh covers it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:10:19 -04:00
Juan DenisandClaude Opus 4.8 5fce53f779 feat(automations): Templates marketplace tab (search, favorites, import)
Promote 'New from template' from a popup to its own Templates tab, built to grow
like a marketplace:
- Responsive card grid (icon, name, description, integration tags) with a
  'Use template' action per card.
- Search box to filter.
- Per-card favorite star; favorites persist in localStorage and float to the
  top (server-side store can replace this later without UI changes).
- 'Import template' action: paste a tramo workflow JSON or load a .json file to
  create it as a new automation.
Removes the old modal picker and its topbar button.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:01:03 -04:00
Juan DenisandClaude Opus 4.8 049235b12d feat(automations): two-column New automation dialog with trigger picker
Replace the name-only New workflow modal with a two-column dialog: an explainer
panel (badge, mini flow diagram, use-case examples) on the left and, on the
right, the name field plus a 'How should it start?' trigger picker. Choices seed
a valid builtin trigger node (manual / cron / panel-event via webhook on
/sk/events / incoming webhook) so the editor opens with the trigger already
placed. Own copy/wording; collapses to a single column on narrow screens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:51:19 -04:00
Juan DenisandClaude Opus 4.8 f4b8630e3b feat(automations): richer template picker
Replace the three plain text rows with proper cards: a brand-tinted icon tile
(Telegram/GitHub/Discord/generic), bold name, muted description, integration
tags, and a hover chevron. Wider modal (size lg) with an intro line, and the
Close button moves to the modal footer slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:42:56 -04:00
Juan DenisandClaude Opus 4.8 6f4246bf0e fix(automations): un-nest template-picker styles from .tramo-page
The 'New from template' picker renders inside a <Modal>, which portals to
<body> -- outside the .tramo-page subtree -- so the .tramo-template rules
nested under .tramo-page never matched it. The name/description spans fell back
to inline defaults and ran together. Move .tramo-template-list/.tramo-template
to top-level scope so the portaled modal picks them up (name bold on its own
line, muted description below).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:33:25 -04:00