This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/ICD/surfaces.md
gobha 10acadc9d0 Changeset 0.38.0 (#233)
Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
2026-03-25 09:20:06 +00:00

4.9 KiB

Surfaces

Dynamic surface lifecycle. Core surfaces are registered at startup. Extension surfaces are uploaded by admins and served from the surface_registry table.

User Endpoints

List Enabled Surfaces

GET /surfaces

Returns enabled surfaces with minimal nav info (no source or enabled fields — these are admin concerns).

{
  "surfaces": [
    { "id": "chat", "title": "Chat", "route": "/" },
    { "id": "editor", "title": "Editor", "route": "/editor" },
    { "id": "hello-dashboard", "title": "Hello Dashboard", "route": "/s/hello-dashboard" }
  ]
}

Auth: Authenticated.

Admin Endpoints

List All Surfaces

GET /admin/surfaces

Returns all surfaces including disabled ones. Each entry is a full Surface Registry Object.

{
  "surfaces": [
    { "id": "chat", "title": "Chat", "manifest": {...}, "enabled": true, "source": "core", "installed_at": "...", "updated_at": "..." },
    { "id": "hello-dashboard", "title": "Hello Dashboard", "manifest": {...}, "enabled": false, "source": "extension", "installed_at": "...", "updated_at": "..." }
  ]
}

Auth: Platform admin.

Get Surface

GET /admin/surfaces/:id

Returns full surface details including manifest.

Response: A single Surface Registry Object.

Errors:

  • 404 — surface not found (or store error).

Auth: Platform admin.

Install Surface

POST /admin/surfaces/install
Content-Type: multipart/form-data

Field: file — a .surface or .zip archive (zip containing manifest.json + static assets). The manifest declares id, title, route, required data loaders, scripts, and CSS.

Size limit: 50 MB.

Manifest validation: id and title are required.

Errors:

  • 400 — no file, wrong extension, too large, invalid zip, missing or invalid manifest, missing id/title.
  • 409 — surface ID conflicts with an existing core surface.

Auth: Platform admin.

Enable / Disable

PUT /admin/surfaces/:id/enable
PUT /admin/surfaces/:id/disable

Disabled surfaces redirect to / and hide from navigation.

Undisableable surfaces: chat and admin cannot be disabled (returns 400).

Errors:

  • 400 — attempting to disable chat or admin.
  • 404 — surface not found.

Auth: Platform admin.

Delete Surface

DELETE /admin/surfaces/:id

Removes the surface, its DB row, and its extracted static assets. Core surfaces cannot be deleted.

Errors:

  • 400 — attempting to delete a core surface.
  • 404 — surface not found.

Auth: Platform admin.

Defense-in-depth: The store layer also enforces WHERE source = 'extension' on delete, so even if the handler check were bypassed, core surfaces are protected.

Surface Registry Object

{
  "id": "hello-dashboard",
  "title": "Hello Dashboard",
  "manifest": { "route": "/s/hello-dashboard", "scripts": [...], "css": [...] },
  "enabled": true,
  "source": "core|extension",
  "installed_at": "...",
  "updated_at": "..."
}

Internal Store Methods

The SurfaceRegistryStore interface exposes methods beyond what the HTTP API uses directly:

  • Seed(ctx, id, title, source, manifest) — upserts a surface at startup. Does not overwrite the enabled flag (admin toggle survives restarts). Called by the page engine's SeedSurfaces().
  • ListEnabled(ctx) → []string — returns IDs of all enabled surfaces. Used by the page engine for nav rendering (EnabledSurfaceIDs()), not exposed via HTTP.

Page Routes

Extension surfaces are served at /s/:slug via RenderExtensionSurface(). The page engine does a runtime DB lookup — no server restart needed after install.

Static assets are served from /surfaces/:id/ (nginx location block in both unified and split deployment). In unified mode, the Go server handles this route with path-traversal protection. In split deployment, nginx serves directly from /data/surfaces/.

Surface Archive Format

A .surface file is a zip archive containing:

manifest.json     — required (must have "id" and "title")
js/               — optional (extracted to /data/surfaces/{id}/js/)
css/              — optional (extracted to /data/surfaces/{id}/css/)
assets/           — optional (icons, images, etc.)
script.star       — optional (Starlark entry point, v0.38.0)
star/             — optional (Starlark submodules, v0.38.0)

The archive may have a single top-level directory prefix (e.g., my-surface/manifest.json) — the installer strips it when locating manifest.json and when extracting js/, css/, assets/, star/ directories and bare .star files. Files outside these directories (e.g., script.js at root) are not extracted.

See EXTENSION-SURFACES.md for the full authoring guide including manifest schema, platform API, and CSS custom properties.