v0.8.4: Documentation refresh + surface sizing fix
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 22s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 7s
CI/CD / test-go-pg (pull_request) Successful in 3m11s
CI/CD / test-sqlite (pull_request) Successful in 3m30s
CI/CD / build-and-deploy (pull_request) Successful in 1m35s
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 22s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 7s
CI/CD / test-go-pg (pull_request) Successful in 3m11s
CI/CD / test-sqlite (pull_request) Successful in 3m30s
CI/CD / build-and-deploy (pull_request) Successful in 1m35s
Docs pass covering v0.7.5–v0.8.3 module additions: - STARLARK-REFERENCE: workspace module, permissions module, settings.has_capability() - EXTENSION-GUIDE: capabilities manifest, vector(N) column, user_permissions, gate_permission, updated permissions list - Tutorial reviewed for v0.7+ accuracy (no changes needed) Surface sizing fix: .surface-inner converted to flex column layout so the shell topbar and surface container share vertical space correctly. All surface containers changed from height:100% to flex:1;min-height:0. Eliminates 44px scroll cutoff on docs, admin, settings, and all extension surfaces. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
42
CHANGELOG.md
42
CHANGELOG.md
@@ -2,6 +2,48 @@
|
||||
|
||||
All notable changes to Armature are documented here.
|
||||
|
||||
## v0.8.4 — Documentation Refresh + Surface Sizing Fix
|
||||
|
||||
Eight versions of module additions (v0.7.5–v0.8.3) shipped without a
|
||||
docs pass. This release brings the public-facing guides up to date and
|
||||
fixes a CSS layout bug affecting all surfaces.
|
||||
|
||||
**Documentation refresh**
|
||||
|
||||
- `STARLARK-REFERENCE.md` — added `workspace` module section (5 builtins),
|
||||
`permissions` module section, and `settings.has_capability()` documentation.
|
||||
- `EXTENSION-GUIDE.md` — added `capabilities` manifest block, `vector(N)`
|
||||
column type, `user_permissions` and `gate_permission` manifest fields,
|
||||
updated sandbox permissions list with v0.8.0+ additions (`files.read`,
|
||||
`files.write`, `workspace.manage`).
|
||||
- `TUTORIAL-FIRST-EXTENSION.md` — reviewed for v0.7+ accuracy (no changes needed).
|
||||
|
||||
**Surface sizing fix**
|
||||
|
||||
All surfaces using the shell topbar had scroll content clipped at the
|
||||
bottom by ~44px (the topbar height). Root cause: surface containers were
|
||||
siblings of `#shell-topbar` inside `.surface-inner`, which used
|
||||
`height: 100%` without flex layout — the surface div claimed the full
|
||||
parent height, ignoring the topbar sibling.
|
||||
|
||||
- Fix: `.surface-inner` now uses `display: flex; flex-direction: column`
|
||||
so the topbar and surface share vertical space via flex layout.
|
||||
- All surface containers (`.surface-docs`, `.surface-admin`,
|
||||
`.surface-settings`, `.surface-editor`, `.extension-surface`) changed
|
||||
from `height: 100%` to `flex: 1; min-height: 0`.
|
||||
- Inline styles on `surface-team-admin` and `welcome-mount` templates
|
||||
updated to match.
|
||||
|
||||
**Modified files:**
|
||||
|
||||
- `server/pages/templates/base.html` — `.surface-inner` gains flex column layout
|
||||
- `src/css/surfaces.css` — `.surface-docs`, `.surface-admin`, `.surface-settings`, `.surface-editor`
|
||||
- `src/css/extension-surface.css` — `.extension-surface`
|
||||
- `server/pages/templates/surfaces/team-admin.html` — inline style fix
|
||||
- `server/pages/templates/surfaces/welcome.html` — inline style fix
|
||||
- `docs/STARLARK-REFERENCE.md` — workspace, permissions, has_capability
|
||||
- `docs/EXTENSION-GUIDE.md` — capabilities, vector, user_permissions, gate_permission
|
||||
|
||||
## v0.8.3 — Vector Column Type
|
||||
|
||||
Extensions can now declare vector columns and perform similarity search.
|
||||
|
||||
34
ROADMAP.md
34
ROADMAP.md
@@ -1,6 +1,6 @@
|
||||
# Armature — Roadmap
|
||||
|
||||
## Current: v0.8.4 — Documentation Refresh + Surface Sizing Fix
|
||||
## Current: v0.8.5 — Extension Composability
|
||||
|
||||
Self-hosted extensible platform kernel. Auth, identity, packages, Starlark
|
||||
sandbox, storage, realtime, and ops are kernel primitives. Everything else
|
||||
@@ -434,31 +434,15 @@ HNSW index on pgvector. `starlarkToGoValue` extended with list→JSON.
|
||||
New: `docs/DESIGN-vector-column.md`. Modified: `ext_db_schema.go`,
|
||||
`db_module.go`, `runner.go`, `extensions.go`, `main.go`. 13 new tests.
|
||||
|
||||
**v0.8.4 — Documentation Refresh + Surface Sizing Fix**
|
||||
**v0.8.4 — Documentation Refresh + Surface Sizing Fix** ✅
|
||||
|
||||
Two unrelated items bundled for a clean changeset before composability.
|
||||
|
||||
*Documentation refresh:* Starlark/extension docs have drifted since v0.7.5.
|
||||
Modules added or significantly changed but not fully documented:
|
||||
- `workspace` module (v0.8.1) — undocumented in STARLARK-REFERENCE.md
|
||||
- `permissions` module (v0.7.7) — undocumented in STARLARK-REFERENCE.md
|
||||
- `settings.has_capability()` (v0.8.2) — not in settings section
|
||||
- `db.query_similar()` vector section (v0.8.3) — added but needs review pass
|
||||
- EXTENSION-GUIDE.md — needs `capabilities` manifest block, `vector(N)` column
|
||||
type, `user_permissions`, `gate_permission`
|
||||
- TUTORIAL-FIRST-EXTENSION.md — review for v0.7+ accuracy
|
||||
|
||||
*Surface sizing bug:* Extension surfaces (`height: 100%` + `overflow: hidden`)
|
||||
don't account for the 44px shell topbar. Content is cut off at the bottom
|
||||
with no scroll. Affected: chat, notes, dashboard, schedules, and likely all
|
||||
extension surfaces. Root cause: `.extension-mount` is `flex: 1` within the
|
||||
viewport column, but extensions set `height: 100%` on their root element
|
||||
which inherits the mount's full height — the topbar's 44px is already
|
||||
subtracted by flex, but the extension's internal layout overflows because
|
||||
child containers also use `height: 100%` + `overflow: hidden`, clipping
|
||||
the last 44px of content. Fix: audit all extension root CSS and ensure
|
||||
inner scroll containers use `flex: 1; min-height: 0; overflow: auto`
|
||||
instead of `height: 100%; overflow: hidden`.
|
||||
Docs pass covering v0.7.5–v0.8.3 additions. STARLARK-REFERENCE.md gains
|
||||
`workspace` module, `permissions` module, `settings.has_capability()`.
|
||||
EXTENSION-GUIDE.md gains `capabilities` manifest, `vector(N)` column type,
|
||||
`user_permissions`, `gate_permission`, updated permissions list. Surface
|
||||
sizing fix: `.surface-inner` converted to flex column layout so the shell
|
||||
topbar and surface container share vertical space correctly — eliminates
|
||||
44px scroll cutoff on all surfaces.
|
||||
|
||||
**v0.8.5 — Extension Composability**
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Every package has a `manifest.json` at its root. Example for a surface:
|
||||
| `icon` | no | Emoji icon for sidebar/menu |
|
||||
| `route` | surfaces | URL path (e.g., `/s/my-surface`) |
|
||||
| `auth` | no | `authenticated` (default) or `public` |
|
||||
| `permissions` | no | Capabilities requested: `db.write`, `http`, `notifications`, `secrets`, `realtime.publish` |
|
||||
| `permissions` | no | Sandbox capabilities: `db.write`, `db.read`, `api.http`, `notifications`, `secrets`, `realtime.publish`, `connections.read`, `workflow.access`, `batch.exec`, `files.read`, `files.write`, `workspace.manage` |
|
||||
| `api_routes` | no | Array of `{method, path}` for extension HTTP endpoints |
|
||||
| `api_schema` | no | OpenAPI documentation for extension API routes (see below) |
|
||||
| `db_tables` | no | Table definitions (see below) |
|
||||
@@ -80,11 +80,23 @@ Every package has a `manifest.json` at its root. Example for a surface:
|
||||
| `exports` | libraries | Functions exported for other packages |
|
||||
| `hooks` | no | Event bus subscriptions |
|
||||
| `config_section` | no | Settings/Admin panel injection (see below) |
|
||||
| `capabilities` | no | Environment requirements (see below) |
|
||||
| `user_permissions` | no | Permissions this extension registers for users (see below) |
|
||||
| `gate_permission` | no | Permission checked before `on_request` executes |
|
||||
| `schema_version` | no | Integer for additive schema migrations |
|
||||
|
||||
## db_tables Schema
|
||||
|
||||
Tables are automatically namespaced as `ext_{package_id}_{table_name}`. Column types: `text`, `int`. Every table gets an auto-generated `id` primary key and `created_at` timestamp.
|
||||
Tables are automatically namespaced as `ext_{package_id}_{table_name}`.
|
||||
Every table gets an auto-generated `id` primary key and `created_at` timestamp.
|
||||
|
||||
Column types: `text`, `int`, `vector(N)`.
|
||||
|
||||
The `vector(N)` type stores N-dimensional float vectors for similarity
|
||||
search via `db.query_similar()`. Storage adapts to the backend:
|
||||
Postgres + pgvector uses native `vector(N)` with HNSW indexes,
|
||||
Postgres without pgvector uses `JSONB`, SQLite uses `TEXT`. N can be
|
||||
1–4096. See the [Starlark Reference](STARLARK-REFERENCE) for query API.
|
||||
|
||||
```json
|
||||
"db_tables": {
|
||||
@@ -137,6 +149,51 @@ Extensions can optionally declare an `api_schema` array in their manifest to pro
|
||||
|
||||
Only `path` and `method` are required. All other fields are optional. Malformed entries are logged and skipped without blocking extension loading.
|
||||
|
||||
## Capabilities
|
||||
|
||||
Extensions can declare environment requirements via the `capabilities`
|
||||
manifest field. The kernel validates these at install time.
|
||||
|
||||
```json
|
||||
{
|
||||
"capabilities": {
|
||||
"required": ["postgres"],
|
||||
"optional": ["pgvector", "workspace"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **required** — install is rejected (HTTP 422) if any capability is missing.
|
||||
- **optional** — install succeeds with a logged warning. Query at runtime
|
||||
with `settings.has_capability("pgvector")` to adapt behavior.
|
||||
|
||||
Detected capabilities: `pgvector`, `workspace`, `object_storage`, `s3`,
|
||||
`postgres`. The admin can view detected capabilities at
|
||||
**Admin > System > Capabilities** or via `GET /admin/capabilities`.
|
||||
|
||||
## User Permissions
|
||||
|
||||
Extensions can register custom permissions that the admin assigns to
|
||||
user groups. This controls access to extension features beyond the
|
||||
sandbox permission model.
|
||||
|
||||
```json
|
||||
{
|
||||
"user_permissions": ["image-gen.use", "image-gen.admin"],
|
||||
"gate_permission": "image-gen.use"
|
||||
}
|
||||
```
|
||||
|
||||
- **user_permissions** — on install, these are merged into the kernel's
|
||||
permission registry. On uninstall, they are removed. The admin assigns
|
||||
them to groups in **Admin > Groups**.
|
||||
- **gate_permission** — if set, the kernel checks this permission before
|
||||
calling `on_request`. Unauthorized users get a 403 without the
|
||||
extension code executing.
|
||||
|
||||
In Starlark, check permissions inline via `req["permissions"]` or call
|
||||
`permissions.check(user_id, "image-gen.use")`.
|
||||
|
||||
## Starlark Sandbox API
|
||||
|
||||
Starlark scripts run server-side with a 1M operation budget and no
|
||||
|
||||
@@ -37,6 +37,17 @@ val = settings.get("theme", "light")
|
||||
The cascade respects the `user_overridable` flag from the package manifest.
|
||||
See [Permissions & Groups](PERMISSIONS-AND-GROUPS) for details.
|
||||
|
||||
```python
|
||||
# Check if a runtime capability is available
|
||||
if settings.has_capability("pgvector"):
|
||||
# Use native vector search
|
||||
...
|
||||
```
|
||||
|
||||
`has_capability(name)` returns `True` if the named environment capability
|
||||
is detected by the kernel. Detected capabilities: `pgvector`, `workspace`,
|
||||
`object_storage`, `s3`, `postgres`.
|
||||
|
||||
### lib
|
||||
|
||||
Load exported functions from library packages.
|
||||
@@ -53,6 +64,20 @@ Requirements:
|
||||
- Results are cached per execution (calling `require` twice returns the
|
||||
same object).
|
||||
|
||||
### permissions
|
||||
|
||||
Check whether a user has a specific permission.
|
||||
|
||||
```python
|
||||
if permissions.check(user_id, "image-gen.use"):
|
||||
# User is authorized
|
||||
...
|
||||
```
|
||||
|
||||
Returns `True` if the user has the permission, `False` otherwise (including
|
||||
when the user is not found). Resolves the user's groups and merges granted
|
||||
permissions — works for both kernel and extension-declared permissions.
|
||||
|
||||
## Permission-gated modules
|
||||
|
||||
These modules are only available if the package has the corresponding
|
||||
@@ -344,6 +369,41 @@ files.delete_prefix("temp/")
|
||||
|
||||
---
|
||||
|
||||
### workspace
|
||||
|
||||
**Permission:** `workspace.manage`
|
||||
|
||||
Managed disk directories for extensions that need a real filesystem
|
||||
(git clones, compilers, media tools). Each workspace is scoped to
|
||||
`{WORKSPACE_ROOT}/{packageID}/{name}/`.
|
||||
|
||||
```python
|
||||
# Create a workspace (idempotent)
|
||||
path = workspace.create("my-repo")
|
||||
# Returns the absolute path to the directory
|
||||
|
||||
# Get the path (None if workspace doesn't exist)
|
||||
path = workspace.path("my-repo")
|
||||
|
||||
# List all workspaces owned by this extension
|
||||
names = workspace.list() # ["my-repo", "cache"]
|
||||
|
||||
# Delete a workspace and all its contents
|
||||
workspace.delete("my-repo")
|
||||
|
||||
# Get disk usage in bytes (10-second timeout)
|
||||
size = workspace.usage("my-repo") # 1048576
|
||||
```
|
||||
|
||||
**Constraints:**
|
||||
- Names must match `^[a-z][a-z0-9_]{0,62}$` (lowercase, no spaces or separators).
|
||||
- Path traversal and symlink escape are blocked.
|
||||
- Quota enforcement via `WORKSPACE_QUOTA_MB` env var (0 = unlimited).
|
||||
- Module not available if `WORKSPACE_ROOT` is unset or not writable.
|
||||
Use `settings.has_capability("workspace")` to check availability.
|
||||
|
||||
---
|
||||
|
||||
## Example: automated stage hook
|
||||
|
||||
A simple hook that reads a setting, queries data, and advances:
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<style>
|
||||
body { margin: 0; background: var(--bg); color: var(--text); display: flex; flex-direction: column; height: 100vh; height: 100dvh; overflow: hidden; padding: env(safe-area-inset-top, 0) env(safe-area-inset-right, 0) env(safe-area-inset-bottom, 0) env(safe-area-inset-left, 0); }
|
||||
.surface { flex: 1; min-height: 0; overflow: hidden; }
|
||||
.surface-inner { width: 100%; height: 100%; overflow: hidden; }
|
||||
.surface-inner { width: 100%; height: 100%; overflow: hidden; display: flex; flex-direction: column; }
|
||||
.banner { flex-shrink: 0; }
|
||||
</style>
|
||||
<meta name="theme-color" content="#0e0e10" id="metaThemeColor">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{{define "surface-team-admin"}}
|
||||
<div id="shell-topbar"></div>
|
||||
<div id="team-admin-mount" class="surface-team-admin" style="height:100%;overflow:hidden;">
|
||||
<div id="team-admin-mount" class="surface-team-admin" style="flex:1;min-height:0;overflow:hidden;">
|
||||
<div class="settings-placeholder" style="padding:40px;text-align:center;">Loading team admin…</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/}}
|
||||
|
||||
{{define "surface-welcome"}}
|
||||
<div id="welcome-mount" style="display:flex;flex-direction:column;height:100%;"></div>
|
||||
<div id="welcome-mount" style="display:flex;flex-direction:column;flex:1;min-height:0;"></div>
|
||||
{{end}}
|
||||
|
||||
{{define "scripts-welcome"}}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
.extension-surface {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
/* ── Docs Surface ────────────────────────── */
|
||||
|
||||
.surface-docs {
|
||||
display: flex; flex-direction: column; height: 100%; overflow: hidden;
|
||||
display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Settings Surface ────────────────────── */
|
||||
|
||||
.surface-settings {
|
||||
display: flex; height: 100%; overflow: hidden;
|
||||
display: flex; flex: 1; min-height: 0; overflow: hidden;
|
||||
}
|
||||
.settings-topbar {
|
||||
display: flex; align-items: center; gap: var(--sp-3);
|
||||
@@ -89,7 +89,7 @@
|
||||
/* ── Admin Surface ───────────────────────── */
|
||||
|
||||
.surface-admin {
|
||||
display: flex; flex-direction: column; height: 100%; overflow: hidden;
|
||||
display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden;
|
||||
}
|
||||
.admin-topbar {
|
||||
display: flex; align-items: center; gap: var(--sp-3);
|
||||
@@ -168,7 +168,7 @@
|
||||
/* ── Editor Surface ──────────────────────── */
|
||||
|
||||
.surface-editor {
|
||||
display: flex; flex-direction: column; height: 100%; overflow: hidden;
|
||||
display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden;
|
||||
}
|
||||
.editor-topbar {
|
||||
display: flex; align-items: center; gap: var(--sp-2);
|
||||
|
||||
Reference in New Issue
Block a user