208 Commits
Author SHA1 Message Date
Juan DenisandClaude Opus 5 8df7350f41 docs(security): stop claiming extension permissions are enforced
Plan 55 Phase 4. EXTENSIONS.md said `permissions` is "**enforced** by the SDK
gate: require_permission(slug, "docker") raises unless docker is declared".
That sentence is true about the function and misleading about the system: the
gate only fires where something calls it, and nothing routes Docker through it.
The SDK exposes no docker, shell, filesystem or network helper at all, and hands
out `db` as raw SQLAlchemy, so an extension that wants any of them imports the
host module directly and no in-process check is involved. Declaring `docker`
does not stop an extension using Docker, and not declaring it does not either.

Both documents now state which capabilities are actually mediated -- today
`agent.command:<action>` and nothing else, because the SDK is the only route to
an agent -- in a per-capability table, and say plainly what declaring does
instead: it tells the operator at install time what the extension claims to
need, and lets them refuse.

SECURITY.md gains an Extension Trust Model section next to the agent one. It
leads with the part that matters most and was never written down: an extension
is in-process code running with the panel's privileges against the panel's
database, nothing sandboxes it, and no permission string changes that. Signing
and sha256 pinning are described for what they are -- integrity of what you
installed, not containment of what it does -- along with the unsigned-prompts,
bad-signature-fails split, and the pip opt-in.

It also explains why the new detail page reports "cannot be observed" rather
than a zero count for unenforceable permissions: absence of evidence is not
evidence of absence, and a "never used" badge there would imply a boundary that
does not exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 17:50:30 -04:00
Juan DenisandClaude Opus 5 c07c73fa5c chore(screenshots): capture the converged Docker, Terminal and Databases tables
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 02:16:47 -04:00
Juan DenisandClaude Opus 5 3d296ff9a4 chore(screenshots): capture the unified incidents table and the SSL conversion
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 01:03:03 -04:00
Juan DenisandClaude Opus 5 36f926ba9a chore(screenshots): capture the selection bar and the fixed KPI band
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 00:19:11 -04:00
Juan DenisandClaude Opus 5 db432afc25 chore(screenshots): refresh captures
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 23:56:54 -04:00
Juan DenisandClaude Opus 5 8eccbf4e23 chore(screenshots): refresh mock-data captures for the converged list pages
Adds projects/vaults/shared-variables shots and drops services-cards, whose
list/cards toggle no longer exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 23:49:52 -04:00
Juan Denis 116c416ec1 @
feat(ui): one row of chrome per table

A table gets exactly one row of chrome:

    [View name v] ....................... [search] [filter] [...]

and nothing else. The row count is gone from it entirely: GridViewPickers
2026-08-12 23:47:58 -04:00
Juan Denis 065b6ed61b docs: consent-gate matrix + seam lifecycle in EXTENSIONS.md 2026-08-05 13:36:33 -04:00
Juan Denis b159c7a5bd docs: WP extraction complete — core data seam, Tier C status 2026-08-05 02:38:41 -04:00
Juan Denis 964f8eee7d docs: hook registration seams + core data seam in EXTENSIONS.md 2026-08-05 01:18:33 -04:00
Juan Denis ef88b765d7 docs: extraction audit reflects phase-4 frontend relocation 2026-08-05 00:48:21 -04:00
Juan Denis a4329124dd chore(extensions): author signing tool + signing spec docs
scripts/sign-extension.mjs signs a bundle with an ed25519 author key;
EXTENSIONS.md documents the format, key model, and verdict behaviors.
Generated private keys live in gitignored scripts/keys/.
2026-08-04 23:34:34 -04:00
Juan Denis 9b531e14b3 Add demo video badge to README variants
Adds a new YouTube “Watch the Demo” badge/link near the top badges in the main README and the Spanish, Portuguese, and Simplified Chinese README translations so users can quickly find the product walkthrough.
2026-08-03 00:17:29 -04:00
Juan Denis 937cbb2b81 feat(setup): capacity-aware install profiles and resource advisory
- install_profile_service: record what install.sh put on the box
  (minimal/standard/full), recommended from detected hardware and
  overridable; thresholds mirrored by recommend_profile in install.sh
- setup wizard: replace the hard ResourceGate/SetupStepTier with
  SetupStepCapacity + SetupStepSecurity
- ResourceAdvisory: inline capacity warning at the point of action
  (WordPress/Docker/Services/Deployments), backed by headroom.fits
- RequiresDocker component for docker-dependent surfaces
- install.sh/update.sh: profile detection + tests
  (test_install.sh, test_update.sh, test_server_capacity.py)
2026-07-31 14:56:37 -04:00
Juan DenisandClaude Opus 5 4effb201f4 fix(ui): rename the page shell off .sk-page (name collision)
components/PageSkeleton.jsx already owned `.sk-page` for its loading
archetypes, including `gap: $space-4`. The new shell reused the name, so every
page built on PageLayout inherited a 16px flex gap between the top bar and the
content — invisible on padded pages, but on Terminal/Logs it showed as a band
of page background under the bar, which is exactly the "weird space" reported.

The shell is `.sk-pageshell` now; the skeleton keeps `.sk-page`. The smoke test
gained a vgap assertion (content top must equal the bar's bottom edge), which
is what would have caught this in the first place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:55:54 -04:00
Juan DenisandClaude Opus 5 fd68d912ce refactor(ui): move the hand-rolled slide-overs onto the DS Drawer
Three detail panels rendered their own `.preview-drawer` — fixed positioning,
their own backdrop, their own slide animation, their own close button — so they
had no focus trap, no escape-to-close, and header chrome that only resembled
the shared drawer's. The Terminal process and service panels and Docker's
container logs panel now use ds/Drawer like everything else.

Left alone deliberately: the File Manager preview (it also renders inline as a
column pane, which a Radix sheet cannot do) and Docker's container inspector
(the `dx-*` workspace has its own language by design).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:50:01 -04:00
Juan DenisandClaude Opus 5 4a7fe8e875 refactor(ui): move Git onto PageLayout
Git hand-rolled the tab-group frame — `page-container--full-bleed sk-tabgroup`
plus its own `__content`/`__inner` wrappers, duplicated across the loading
branch and the main return. It renders through the shared layout now, so the
frame comes from one place and the loading state matches the loaded page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:33:16 -04:00
Juan DenisandClaude Opus 5 9b27b2e362 refactor(ui): run Terminal/Logs as a flush workspace on the shared shell
The console panes were rounded, bordered cards floating inside a `0 16px 16px`
inset — the only page framed that way, which read as stray side padding plus
stray corners against an otherwise flush layout. It now renders through
PageLayout in `fill` mode, like the File Manager: the panes fill the content
region edge-to-edge and keep their internal dividers. Services stays a card
grid, so it re-pads itself instead of running to the edges.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:33:16 -04:00
Juan DenisandClaude Opus 5 f7ff4df043 fix(cron): pad the create drawer's body
`.sk-drawer__body` is the scroll region and deliberately carries no padding —
some drawers run edge-to-edge — so each body pads its own root. `.cron-form`
did not, leaving every label and input flush against the panel edges. Same
inset as `.cron-drawer` so the two cron drawers line up, plus a note on the DS
Drawer so the next body does not repeat it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:33:05 -04:00
Juan DenisandClaude Opus 5 62507f750c feat(cron): rebuild the create surface as a drawer on SchedulePicker
Creating a job went through a plain modal with a preset <select> and a bare
cron text field — the schedule was something you had to already know how to
write. It now opens a drawer built on the shared SchedulePicker (presets ·
builder · advanced, with the humanized cadence and the next fire times), plus a
preview of the exact crontab line the form will write.

The drawer owns its own form state, so the page drops the form object, the
reset/open/submit handlers and the /cron/presets fetch that only fed the old
dropdown. The list head gains the section title and the "N of M jobs" count,
with the filter moved to the right where the mock has it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 22:08:03 -04:00
Juan DenisandClaude Opus 5 e55968f177 docs(screenshots): capture the new Monitoring surfaces
Monitors, a monitor's detail and 90-day uptime strip, Incidents, Events and
Jobs. These double as the render smoke test for the pages this round added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:34:25 -04:00
Juan DenisandClaude Opus 5 e172289850 docs(screenshots): show the app, not just the page
The dashboard shots were clipped to `.dashboard-page`, which cropped the
sidebar out and made them the only images in the set without the app chrome
around them — next to Services and Docker in the README it read as a bug.

The clip was there for a reason: `.main-content` owns the scroll, so a plain
shot stops at the viewport and `fullPage` does not reach the bottom widgets.
The fix is a per-section viewport instead — a taller frame captures the whole
board WITH the navigation, and `capture.mjs` now honours `section.viewport`
for any page with the same internal-scroll shape.

Also stops the shots claiming work is in flight. The dashboard fixtures
answered the deploy pill with running jobs, so every capture carried a
"2 deploys running" pill over the sidebar. The pill is correct behaviour; it
just has nothing to do with a screenshot of an idle board, so the fixture now
reports settled jobs rather than the CSS hiding a true statement.

31/31 sections still render.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 11:16:44 -04:00
Juan DenisandClaude Opus 5 640e7e827e docs(screenshots): recapture, and fix two bugs the capture exposed
Six new sections cover the pages rebuilt this round — the dashboard as it ships,
its edit mode, the widget library, the widget editor, the databases explorer and
the engine catalog drawer. 31/31 sections render.

The capture is a render smoke test, not just a picture, and it earned that here
by surfacing two real bugs that no test could see:

`SearchField` merges its `className` onto its OWN root, so `.skw-lib__search`
IS the `.sk-searchfield` element rather than a wrapper around one. The rule
styling it as a parent matched nothing, leaving the shared `flex: 1 1 240px`
in force — and inside a column-direction filter bar that 240px basis is a
HEIGHT. The widget library's search box was rendering 240px tall.

`GET /auth/me` answers `{user: {...}}`, but Databases read `.role` straight off
the envelope, so `isAdmin` was always false. Every admin got the read-only
console — and with the DDL builder now shipped, would never have seen the button
that runs a CREATE TABLE.

Also carries `postgresql.yaml`, which I left out of the extensions commit.
Without `engine.extensions: true` stock PostgreSQL is not a host for pgvector at
all; the extension tests passed only because the working tree had the change the
commit did not.

Fixture note for whoever edits these next: `/metrics/history` points are NESTED
(`cpu.percent`, `load['1m']`) and carry no network data, `/servers/available` is
a bare array, and `DeployPill` queries `/deployment-jobs` twice and sums — a
single unfiltered payload makes it claim eight running deploys.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 09:59:06 -04:00
Juan Denis b5160e90bf chore: clean root and docs — drop recovery artifacts, relocate stale audits
- Remove patch_localhost.py (one-off patch script, served its purpose)
- Remove RECOVERY.md + docs/RECOVERED_COMMITS.txt (July-6 incident artifacts)
- Move completed audits and Jan-era strategy docs to docs/audits/
- Update .gitignore for relocated UI_CONSISTENCY_AUDIT.md
2026-07-25 22:22:33 -04:00
Juan Denis 2d3bb33a34 docs: expand README requirements beyond Ubuntu, note homelab/ARM support
- List all installer-supported distros (Ubuntu 22.04+, Debian 12+,
  Fedora, RHEL/Rocky/AlmaLinux 9+) instead of Ubuntu only
- Add Arch row (x86_64, ARM64) matching installer platform detection
- Clarify Docker is optional for the panel itself
- Note the panel's ~180 MB RAM / ~500 MB disk footprint and that it
  runs on 1 GB VPSs, spare laptops, and Raspberry Pi homelabs
2026-07-25 20:40:11 -04:00
Juan DenisandClaude Opus 5 92e0750262 feat(sdk): a second widget surface — types users place, not just slots
`contributions.widgets` mounts a component at a host-chosen slot. That is the
wrong shape for the new dashboard, where the user decides what goes on a board
and where. So this adds `dashboard_widgets` alongside it: a registry of
placeable widget TYPES that surface in the widget library, each instance
positioned, sized and configured by the person looking at it.

The two are easy to confuse, so the docstring, the manifest spec and
EXTENSIONS.md all now say plainly which is which, and a test asserts a
dashboard widget never leaks into the fixed-slot list.

SDK_VERSION 1.0.0 -> 1.1.0 (added surface, older extensions unaffected),
bumped in lock-step across the JS export and the backend mirror.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:24:41 -04:00
Juan DenisandClaude Opus 5 8e6b53d77b refactor(servers): one list instead of a rail, and give Add Server its drawer back
The fleet list carried a rail of its own — a health dial, a status nav and a
groups nav — plus a workbar, a command bar, bulk-select and a per-row action
menu. Every number those showed is a column or a filter here, so the page is
one filter row and one table: status segments with counts, a group select,
"4 of 4 servers · 2 online", then Server / Host / Agent / Status / CPU /
Memory / Disk / Last seen. Row actions are gone because ping and delete
already live on the server's own page, which is where rebooting or deleting a
machine belongs.

Offline and pending rows show a dash rather than empty gauges: a 0% bar is a
claim, and those hosts are not reporting.

Add Server was written as a right-side drawer, then pointed at the centred
Dialog primitive while keeping its own `position: fixed; right: 0` geometry.
The primitive's `left: 50%` and -50% transform still applied on top, which is
what rendered the panel half off-screen and clipped. It runs on the shared
Drawer now, so the slide-in, focus trap and overlay come from one place.

`.servers-page { padding: 0 }` was a leftover from the old full-bleed
workspace and was overriding the tab group's padding, running the table under
the right edge of the viewport. Removed, with its layout hook.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:03:48 -04:00
Juan DenisandClaude Opus 5 98ea0f6b10 feat(backups): port the design's Overview, Schedules, Snapshots and Storage
The summary band rendered above every section of the group, so "Total
backups" sat on top of Schedules, Storage and Settings, where it answered
nothing you opened those tabs to ask. It belongs to one section, and that
section is now an Overview rather than the archive table.

Overview answers "is my data safe?": protected resources, storage used,
success rate and next scheduled; an 18-week activity heatmap with its failure
days marked; a recent-activity feed; destination bars; latest snapshots. This
needed two endpoints the frontend had never called — /backups/policies and
/backups/runs — which is where the heatmap, the success rate and the
durations come from. A panel that has only ever taken manual backups falls
back to the archive listing instead of showing an empty page.

Schedules gets the mock's columns (frequency, retention, destination, next
fire, an on switch) and only its own action in the top bar; Create Backup was
answering a question nobody on that tab was asking. Snapshots moves its
search to the top bar with every other list page. Storage becomes destination
cards over the provider form.

Two places the mock has data we do not, left out rather than faked: per-policy
size and run-now on Schedules (the schedule API exposes neither), and
duration on Snapshots (filesystem entries do not record it).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:03:34 -04:00
Juan DenisandClaude Opus 5 b413148194 feat(monitoring): fold Fleet Monitor in, and scope the page to a server
Fleet Monitor and /monitoring were the same questions at two scopes, so they
were two pages restating each other: both grew an alerts surface and both
grew thresholds. They are now one page with a scope, not two pages with a
gap between them.

The top bar carries a server picker, URL-backed as ?server= so the choice
survives a tab change, a refresh and a pasted link. Overview, Alerts and
Capacity all follow it. The fourth KPI tile is whichever reading the current
scope actually has — load average on the panel host, container count on an
agent — rather than a blank where the other one would be.

The overview is ported from the design mock: KPI band with trends and
sparklines, two trend charts, and a Host health strip below. That strip is
what the standalone heatmap became, except each card is a button that
re-scopes everything above it instead of a cell you can only read.

There is deliberately no second tab strip inside the page. Overview / Alerts
/ Rules / Capacity / Doctor live in the group's own top bar, the way the mock
puts them — a nav under a nav reads as two competing headers.

Also: the sidebar said "Observability" for a page, a route and a title that
all said "Monitoring", and the delivery channels rendered Discord, Slack and
a raw webhook with the same generic glyph. Both fixed.

/fleet-monitor redirects; the Servers tab strip loses the entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:03:15 -04:00
Juan DenisandClaude Opus 5 d944b695e7 refactor(ui): one place for search, and drop the KPI restatements
Search now lives in the top bar, right-most, on every list page — Services
kept its copy in the table toolbar, and Domains had none at all. The view
switch claims the free space so it stays right-aligned where a page gave its
toolbar search up; where one is still present it hands the margin back.

Domains loses its four-tile KPI strip. Every number on it was already the
count of a segment you would click to act on, so the segments carry them
instead: All 5 / Expiring SSL 1 / Attention 1. Search narrows the set the
counts come from, so they always describe what a click would show.

Fleet Proxy loses its title, subtitle and five-tile band — the tab strip
names the page and every tile re-counted a column of the table beneath it.
What the page is for now lives in the empty state, where it is needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:02:58 -04:00
Juan DenisandClaude Opus 5 593dd11a8b docs(registries): write down the test for what earns a registry
"Should X be a registry too?" gets re-asked every few months, and the answer is
usually no. This records the rule and the per-catalog verdicts so the assessment
does not get redone from scratch.

A catalog earns a registry only when all three hold: it changes faster than the
panel releases, it is data rather than code (a new entry must work on an
existing panel with no code change), and someone outside the release cycle would
contribute to it. Extensions, themes and app templates pass. Seven other
catalogs were assessed and rejected, each with the reason -- server templates,
notification events, AI providers, DB tuning settings, sanitization profiles,
agent survey probes and build packs.

Also names the failure mode that makes a bad extraction expensive: panels that
have not upgraded read the registry forever, so an entry that needs new panel
code either gets ignored or renders as a broken item. That is what forces a
min_panel_version gate, and why serverkit-extensions has one and the other two
do not need one.

Records one open candidate rather than leaving it implicit. YARA rules pass all
three tests and have no update path -- 16 curated rules frozen at release time.
That got sharper this session: the image now builds without ClamAV by default,
and ClamAV was the component with a self-updating feed via freshclam, so YARA is
now the only malware detection in a default container. Operators can already
drop custom .yar files in CUSTOM_RULES_DIR; what is missing is a curated
upstream. Not built -- noted so the gap is deliberate.

Adds the templates registry and this doc to the README documentation table.
Every claim was checked against the code rather than written from memory: rule
counts (16 in both BUILTIN_RULES and webshells.yar), the CUSTOM_RULES_DIR
default, ARG INSTALL_CLAMAV=false, and that ServerTemplateService already
exposes create_template so its 12-entry library is a seed, not a catalog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 14:53:17 -04:00
Juan DenisandClaude Opus 5 dd402892e1 docs(readme): lead with what the panel actually measures
Adds a "By the Numbers" table under the badges in all four READMEs (EN, ES, PT,
zh-CN). Every figure is measured from this repo at this commit, not estimated:

  1,519 REST endpoints   walked from app.url_map after create_app(); a raw
                         @route grep undercounts at 1,276 because plugins
                         register routes dynamically
    106 app templates    backend/templates/*.yaml, matches index.json
  2,633 backend tests    pytest tests --collect-only
1.75 MB gzipped UI       frontend/dist, gzip -6 over every asset
 ~180 MB resident        gunicorn --workers 1 --threads 100, FLASK_ENV=production
 501 MB image            docker build . on linux/amd64

Three claims were corrected against the code rather than written from memory:
the OpenAPI docs live at /api/v1/docs (not /api/docs); "no telemetry" was wrong
-- there is a telemetry subsystem, it just writes local SystemEvent rows -- so
it now says nothing phones home, which was verified (no sentry/posthog/segment/
mixpanel, no version ping, and the SPA's only external URLs are documentation
links, not asset loads).

Numbers only ever grow here, so a stale figure undercounts rather than
overstates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 14:15:44 -04:00
Juan DenisandClaude Opus 5 df3524b202 docs(extensions): document the six contribution surfaces
The SDK table listed the surfaces an extension consumes but not the ones it can
now contribute to, and deploys had been missing since it shipped. Adds all six
with one worked example each, plus the rule that ties them together: namespace
what you register, the panel owns the UI and normalises what you hand back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 14:03:01 -04:00
Juan DenisandClaude Opus 5 374404e43d fix(ui): let drawer footers reach the bottom edge
The shared sheet sets padding and a gap on the panel itself, but
.sk-drawer lays out three full-height regions inside it — head, body,
foot — each managing its own padding. The panel's padding therefore inset
all of them: the header's divider stopped short of both edges and the
footer floated above a band of dead space instead of sitting on the
bottom edge.

Zero the panel's padding and gap, and give scrolling to __body alone —
with the panel itself scrollable the footer could not stay put either.

Fixed at .sk-drawer rather than per page, so every drawer gets it: the
template deploy and filter drawers had the same gap.

Measured after the slide-in settles: 0px below the footer, and head/foot
now span the full panel width.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:12:59 -04:00
Juan DenisandClaude Opus 5 5b26e427a1 fix(wordpress): create a site from the same drawer as everything else
"Create WordPress Site" was a centred modal while services and templates
deploy from a right-hand drawer, and its own styling was broken: an icon
stranded on its own line, an unspaced list running straight into the
first field, and a footer that floated over the form.

Two causes, both structural. It rendered legacy .modal-body and
.modal-footer *inside* the shared Modal, which already supplies a body
and takes a footer prop — the chrome was nested twice. And its
.install-warning callout is styled under `.wordpress-page`, while the
dialog renders in a portal, so it is not a descendant of the page that
opened it and none of those rules ever applied.

So the drawer styling moves out of _templates.scss into the design system
as .sk-formdrawer, shared by both pages. That is the fix for the class of
bug, not just this instance: a portalled surface cannot rely on
page-scoped SCSS, so its styles belong somewhere unscoped. The block also
now covers bare <select> and checkbox rows, which otherwise render with
the browser's own white chrome on a dark panel, and the callout.

Verified in a browser: drawer settles at the right edge at its declared
560px, callout renders with its background and spacing, selects pick up
the surface colour, and no .modal-body/.modal-footer/.install-warning
remain in the tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:07:06 -04:00
Juan DenisandClaude Opus 5 6508c15ddb docs: screenshot the template card-click deploy drawer
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 11:35:05 -04:00
Juan DenisandClaude Opus 5 40cef81b3c docs: capture the loading-skeleton archetypes and refresh shots
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 05:41:15 -04:00
Juan DenisandClaude Opus 5 5b1403dfaa feat(ui): shape loading skeletons to the page that is loading
Every one of the ~40 loaders went through EmptyState's single hardcoded
shape — an avatar, three cards, three lines — whether the page was a
table, a console, a form or a chart. A skeleton exists to predict the
layout about to arrive so that arrival is a fill-in rather than a jump;
one shape reused everywhere mispredicts on all but the page it was drawn
for, which is worse than honest, because it promises a layout that never
comes.

PageSkeleton provides ten layout archetypes — panel, table, feed, cards,
detail, console, form, chart, tree, split — driven by SCSS so widths stay
responsive instead of being guessed in pixels. Repeated lines use a
deterministic ragged width, so they read as text and don't reshuffle each
render the way Math.random() would.

EmptyState takes `loadingVariant` (defaulting to the old `panel`, so
nothing regresses) and 41 call sites now name their real shape.
ResourceListPage picks table or cards from its own view state.

All ten are on the style guide under Feedback & Loading, and captured as
one screenshot so a regression in any of them is visible in one shot.

Captured bones (npm run capture:skeletons) remain the pixel-exact option
for individual regions; these archetypes are the broad-coverage answer
for the pages that will never be worth capturing one by one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 04:46:16 -04:00
Juan DenisandClaude Opus 5 1254dda17f docs: refresh Services-group screenshots + changelog
Recaptured against the ported design, plus new shots for the templates
catalog, its filter and deploy drawers, and the Services card view.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 04:40:35 -04:00
Juan DenisandClaude Fable 5 f2cf89563a fix(deployments): full-width jobs table + console polish (plan 51.5 UI pass)
/deployments still reserved a second grid column for the detail pane plan 51
removed — the table sat at half width with its Started column clipped by
overflow:hidden. Single full-width panel now; pruned ~150 lines of dead
detail-pane SCSS; status badge + progress colors move from inline styles to
modifier classes; kinds humanized; skeleton loading rows; removed the topbar
New Service button duplicating the adjacent tab. Console: fills the viewport
(calc-based min-height), wider step rail (248px), flexible log search, denser
toolbar. Adds deploy-console screenshots (4 sections, mock-data captures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:11:55 -04:00
Juan DenisandClaude Fable 5 31d13c6370 docs: theme screenshots live only in the serverkit-themes registry
Drop docs/screenshots/themes/ from the panel repo; THEMING.md and the
README roadmap now link to the registry's gallery instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 12:16:40 -04:00
Juan DenisandClaude Fable 5 24ee204294 docs(themes): add live screenshots of all 19 themes + gallery page
One dashboard capture per theme (mock-data rig, capture-themes.mjs) under
docs/screenshots/themes/ with a grouped gallery README, linked from
THEMING.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:42:14 -04:00
Juan Denis 8180ff4474 docs(mcp): recommend Faro Agent Bridge + faro-cli as primary server access 2026-07-23 08:55:57 -04:00
Juan DenisandClaude Opus 4.8 e09a6c2948 feat(themes): registry browse, import, panel default, and Theme Studio (plan 60 Phase 2-4)
- ThemeContext: fetch pre-auth panel default (/public/active) and follow it
  until a user picks a skin; ThemeSync bridge loads installed themes after auth
- Theme Gallery admin actions: set panel default, remove installed themes
- Browse modal: install community themes from the registry (offline-tolerant)
- Import theme.json (admin, paste/upload)
- Theme Studio: grouped color editor over the LIVE panel, start-from any
  installed theme, Export theme.json, Save-to-panel, and Submit = a prefilled
  GitHub new-file PR against serverkit-themes
- docs/THEMING.md (token reference + rules + submit flow); README docs table +
  ROADMAP "Recently Completed"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 00:24:10 -04:00
Juan Denis e8d84297f2 Marketplace trust: split 409 reasons, hide unreviewed outside dev mode
Review feedback + follow-up on the review-stamps feature:

- The 409 trust gate now distinguishes reason 'unreviewed' (community
  code not vetted) from 'unverified' (no pinned checksum, possible even
  for first_party), each with an accurate message; the risk dialog
  carries the reason and matches its title/body to the actual case
  instead of always claiming 'unreviewed community extension'
- Unreviewed registry entries are now developer-stage content: hidden
  from the Marketplace catalog and refused at the install endpoint
  (404) unless the panel runs in a development context (Flask debug /
  testing config, or the dev_mode setting). Hidden means not
  installable; the 409 acknowledgment flow only exists where they list
- dev_mode setting description mentions the Marketplace effect
- tests: reason assertions on both 409 cases; catalog visibility and
  404-on-hidden-install coverage (38 passing)
2026-07-22 23:18:33 -04:00
Juan Denis 02bb7666e2 Marketplace: hash-bound review stamps and trust levels for registry extensions
The registry index (schema v3) can now carry a review object per entry
(reviewer, date, sha256, optional commit/notes) asserting that someone
inspected the exact artifact being listed. The stamp is bound to the
artifact hash: review.sha256 must equal the entry sha256, so any change
to the released zip invalidates the review.

- registry_service: keep review through normalization (validated),
  derive trust per entry: first_party, reviewed (stamp matches the
  pinned hash), or unreviewed
- marketplace install route: unreviewed (or sha256-less) entries return
  409 requires_acknowledgment unless the admin posts
  acknowledge_risk: true; acknowledged installs record trust in the
  audit log. Consent gate, consistent with ADR 0002
- Marketplace UI: Reviewed badge (reviewer + date tooltip) and
  Unreviewed warning badge on registry cards, plus a risk confirmation
  modal that retries the install with acknowledge_risk
- tests: trust derivation and the install gate (409 + acknowledge path)
- docs: EXTENSIONS_REGISTRY.md documents the field and the flow
2026-07-22 22:45:57 -04:00
Juan DenisandClaude Opus 4.8 fb19778e7f docs(wordpress): correct extraction audit — frontend not yet extracted
The audit overstated the WP frontend as contributed by the extension; in the
tree the extension index.jsx is still a re-export shell over core pages/
components/SCSS. Corrected to match reality before plan 52 Phase 4 moves it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:31:29 -04:00
Juan DenisandClaude Opus 4.8 ffc1fd7728 feat(extensions): deliver runtime ESM frontends to production panels (#39)
Wire the server half of runtime-frontend delivery: get_active_contributions()
now emits panel sdk_version and a 'frontends' descriptor map {slug: {entry,
hashes, sdk_version}} for every active extension whose frontend_entry is a .mjs
ESM bundle. The bundle sha256 is pinned at install time into the plugin config
under the reserved _frontend_hashes key (no migration — reuses config_json);
update_plugin_config() and the config API preserve/hide reserved keys. Add the
extensions.runtime_frontend kill switch (default on). The client loader already
consumed this shape, so an installed extension's UI now renders on a pre-built
panel with no Vite rebuild. Un-skips the restored plan-25 recovery suites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:24:36 -04:00
Juan DenisandClaude Opus 4.8 6bf8c87b9d docs(deploy): document the Deploy Console; update CHANGELOG and ROADMAP
Add docs/DEPLOY_CONSOLE.md (the feature + architecture), an architecture section
in docs/ARCHITECTURE.md, a CHANGELOG (Unreleased) entry, and move the Deploy
Console into the ROADMAP's Recently Completed list with its Active Development
checkboxes ticked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:43:50 -04:00
Juan Denis d80518966b Add Trendshift badge to all README variants
Adds the Trendshift repository badge to the main README and the Spanish, Portuguese, and Simplified Chinese README files so project visibility/trend metrics are shown consistently across all language entry points.
2026-07-22 09:08:26 -04:00