Docs expand v0.14.x sidecar tier + v0.15.x polish in roadmap
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
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 5s
CI/CD / test-go-pg (pull_request) Successful in 2m47s
CI/CD / test-sqlite (pull_request) Successful in 3m11s
CI/CD / build-and-deploy (pull_request) Successful in 1m26s
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
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 5s
CI/CD / test-go-pg (pull_request) Successful in 2m47s
CI/CD / test-sqlite (pull_request) Successful in 3m11s
CI/CD / build-and-deploy (pull_request) Successful in 1m26s
ROADMAP.md: v0.14.x expanded from 3→6 versions (sidecar registry, capabilities, event bus, manifest, reference sidecar, user sidecars). v0.15.x split out as polish+stability (dialog audit, migration CLI, schema freeze). v1.0.0 gate adds sidecar criteria. Design decisions log updated with sidecar rationale. New docs: DESIGN-sidecar-v014x.md (full sidecar design), ROADMAP-v010x-v100.md (detailed version plan through 1.0). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
35
ROADMAP.md
35
ROADMAP.md
@@ -233,13 +233,31 @@ capability. Zero configuration.
|
||||
|
||||
---
|
||||
|
||||
### v0.14.x — Sidecar Tier + Polish
|
||||
### v0.14.x — Sidecar Tier
|
||||
|
||||
Connect-inward model: sidecars connect TO the kernel (no K8s RBAC, no
|
||||
service mesh, no DNS discovery). Instance sidecars (shared infrastructure)
|
||||
and user sidecars (personal local tools). Design doc:
|
||||
`docs/DESIGN-sidecar-v014x.md`.
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.14.0 | Sidecar Tier |
|
||||
| v0.14.1 | Native Dialog Audit |
|
||||
| v0.14.2 | Stability + Migration Tooling |
|
||||
| v0.14.0 | Sidecar Registry + Auth |
|
||||
| v0.14.1 | Capability Registration + Execution |
|
||||
| v0.14.2 | Kernel API Access + Event Bus |
|
||||
| v0.14.3 | Manifest Integration + Admin Polish |
|
||||
| v0.14.4 | Reference Sidecar (`armature-embed`) + Instance Gate |
|
||||
| v0.14.5 | User Sidecars + Reference (`user-bridge`) + User Gate |
|
||||
|
||||
---
|
||||
|
||||
### v0.15.x — Polish + Stability
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.15.0 | Native Dialog Audit |
|
||||
| v0.15.1 | Versioned Migrations + `armature migrate` CLI |
|
||||
| v0.15.2 | Pre-1.0 Schema Freeze + Upgrade Path Validation |
|
||||
|
||||
---
|
||||
|
||||
@@ -266,6 +284,10 @@ Gate criteria:
|
||||
- Headless E2E green on PG + SQLite
|
||||
- `armature-ca.sh` + mTLS deployment guide
|
||||
- Single-binary + Docker + K8s deployment paths documented
|
||||
- Instance sidecar (`armature-embed`) deployed and functional
|
||||
- User sidecar (`user-bridge`) functional on macOS/Linux/Windows
|
||||
- Token + mTLS sidecar auth both tested
|
||||
- `armature migrate` CLI functional
|
||||
|
||||
---
|
||||
|
||||
@@ -329,3 +351,8 @@ These are candidates, not commitments. Each requires a design doc.
|
||||
| Layered prompt architecture (6 layers) | Admin safety not overridable. Clear separation: platform → space → character → context → tools → user. |
|
||||
| Personas in llm-bridge, not chat | Chat sees AI as just another participant_type. Persona identity is llm-bridge's concern. |
|
||||
| llm-bridge after notes + chat (v0.13.x) | Proves the composability hooks work without being designed for a specific consumer. |
|
||||
| Sidecar connect-inward | No K8s RBAC, no service mesh, no DNS discovery. Works on any deployment target. |
|
||||
| Sidecar HTTP/JSON, not gRPC | KISS. 4-endpoint contract. Every language has HTTP. |
|
||||
| User sidecars in v0.14.5 | Prove instance contract first, extend to users with same mechanism. |
|
||||
| Three-layer user sidecar RBAC | Admin controls who + what, user controls visibility. Defense in depth. |
|
||||
| Separate polish (v0.15.x) | Security-critical sidecar infra and quality-of-life polish shouldn't share focus. |
|
||||
|
||||
922
docs/DESIGN-sidecar-v014x.md
Normal file
922
docs/DESIGN-sidecar-v014x.md
Normal file
@@ -0,0 +1,922 @@
|
||||
# DESIGN: Sidecar Tier — v0.14.x
|
||||
|
||||
## Status: Proposed
|
||||
|
||||
## Problem
|
||||
|
||||
Starlark is the right sandbox for most extension logic: declarative,
|
||||
safe, fast to start, no deployment pipeline. But some workloads cannot
|
||||
run in Starlark:
|
||||
|
||||
- **ML inference** — Python + PyTorch/TF model loading, GPU access.
|
||||
- **Media transcoding** — ffmpeg, ImageMagick, Whisper.
|
||||
- **Language servers** — LSP processes for code-workspace.
|
||||
- **Local git** — clone, diff, merge operations on workspace directories.
|
||||
- **Custom runtimes** — anything needing system libraries, native code,
|
||||
or long-running processes.
|
||||
- **Personal local bridges** — a user's laptop exposing filesystem,
|
||||
terminal, clipboard, or local compute to the Armature instance.
|
||||
|
||||
Today, the `http` module lets Starlark call external APIs (`api.http`
|
||||
permission, domain allowlists, SSRF protection). This covers cloud
|
||||
services (OpenAI, S3, external webhooks) but not **co-located processes**
|
||||
that need access to Armature's data, events, and workspace filesystem,
|
||||
nor **personal processes** on a user's own machine.
|
||||
|
||||
The sidecar tier bridges this gap: out-of-process extensions that
|
||||
connect inward to the kernel, authenticate, register capabilities, and
|
||||
participate in the extension ecosystem as first-class citizens — at both
|
||||
the **instance level** (shared infrastructure) and the **user level**
|
||||
(personal local tools).
|
||||
|
||||
**If done wrong, the consequences are severe:**
|
||||
|
||||
- Wrong auth model → arbitrary processes access kernel APIs.
|
||||
- Wrong lifecycle → resource leaks, zombie processes, orphaned
|
||||
registrations.
|
||||
- Wrong API surface → every sidecar extension inherits the debt.
|
||||
- Wrong isolation → a runaway sidecar takes down the cluster.
|
||||
- Wrong discovery → fragile deployments that break on restart.
|
||||
- Wrong scoping → user sidecars see other users' data.
|
||||
|
||||
This is invisible infrastructure. Users never see it. But every
|
||||
extension that needs native code depends on the contract being right.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- **Kernel manages sidecar processes.** The kernel does not start, stop,
|
||||
or restart sidecars. That's the deployment layer's job (K8s, Docker
|
||||
Compose, systemd, or a human running a binary). The kernel discovers
|
||||
sidecars that connect to it.
|
||||
- **Sidecar marketplace.** Sidecar packages are installed like any other
|
||||
`.pkg` file. The sidecar binary ships separately (container image,
|
||||
standalone binary, pip package). The manifest declares the sidecar
|
||||
endpoint; the operator deploys it.
|
||||
- **Arbitrary bidirectional streaming.** Sidecars communicate via
|
||||
HTTP/JSON request-response and WebSocket events. No gRPC, no custom
|
||||
binary protocols. KISS.
|
||||
- **Hot code reload.** Sidecar updates require process restart. The
|
||||
kernel detects the reconnection and re-registers capabilities.
|
||||
- **Multi-tenant sidecar isolation.** Instance-level sidecars run at the
|
||||
instance level, not per-user. User-level isolation is handled by
|
||||
user sidecars (v0.14.5), which are scoped to the owning user's
|
||||
identity.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Connect-Inward Model
|
||||
|
||||
The defining property: **sidecars connect TO the kernel, not the other
|
||||
way around.** The kernel never reaches outward to discover or contact
|
||||
a sidecar. This eliminates:
|
||||
|
||||
- K8s RBAC for the kernel to discover pods.
|
||||
- Service mesh configuration.
|
||||
- DNS-based service discovery.
|
||||
- Any notion of the kernel "managing" external processes.
|
||||
|
||||
A sidecar is just a process that knows the kernel's address and has
|
||||
credentials to authenticate. It could be a container in the same pod, a
|
||||
separate deployment, a systemd service on the same machine, or a process
|
||||
on a developer's laptop connected via tunnel.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Deployment Layer (K8s / Docker / systemd / manual) │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ instance │ │ instance │ │ instance │ │
|
||||
│ │ sidecar: │ │ sidecar: │ │ sidecar: │ │
|
||||
│ │ ml-infer │ │ ffmpeg │ │ git-ops │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ │ connect inward │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────────────────────────────────┐ │
|
||||
│ │ Armature Kernel │ │
|
||||
│ │ ┌─────────────────────────────────────┐ │ │
|
||||
│ │ │ Sidecar Registry (PG) │ │ │
|
||||
│ │ │ ml-infer: instance, 3 caps │ │ │
|
||||
│ │ │ ffmpeg: instance, 2 caps │ │ │
|
||||
│ │ │ git-ops: instance, 4 caps │ │ │
|
||||
│ │ │ jeff-local: user:jeff, 3 caps │ │ │
|
||||
│ │ └─────────────────────────────────────┘ │ │
|
||||
│ └────────────────────────────────▲─────────┘ │
|
||||
│ │ │
|
||||
└───────────────────────────────────┼──────────────────┘
|
||||
│ connect inward
|
||||
┌─────┴──────┐
|
||||
│ user │
|
||||
│ sidecar: │
|
||||
│ jeff-local │
|
||||
│ (laptop) │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
### Two Scopes
|
||||
|
||||
**Instance sidecars** — shared infrastructure. Deployed by the operator.
|
||||
Any user's requests can invoke their capabilities. An ML inference
|
||||
sidecar, a transcoding service, a git operations worker.
|
||||
|
||||
**User sidecars** — personal processes. Run by an individual user on
|
||||
their own machine. Only the owning user's requests can invoke their
|
||||
capabilities. A local file bridge, a personal Ollama instance, a
|
||||
script that reacts to Armature events.
|
||||
|
||||
Both use the same connect-inward mechanism, same registration flow,
|
||||
same capability contract. The difference is the identity binding and
|
||||
access scoping.
|
||||
|
||||
### Two Authentication Modes
|
||||
|
||||
#### Mode A: Registration Token
|
||||
|
||||
For simple deployments (Docker Compose, single machine, dev/test).
|
||||
|
||||
1. Admin generates an instance sidecar token:
|
||||
`armature sidecar token create --package ml-inference`
|
||||
User generates a personal sidecar token (from settings UI or CLI):
|
||||
`armature sidecar token create --personal`
|
||||
2. Token is a signed JWT with claims: `{package_id, scope, user_id, exp}`.
|
||||
Instance tokens: `scope: "instance"`, `user_id: null`.
|
||||
User tokens: `scope: "user"`, `user_id: "<owner>"`.
|
||||
3. Sidecar starts with the token as env: `ARMATURE_TOKEN=ey...`
|
||||
4. Sidecar calls `POST /api/v1/sidecar/register`.
|
||||
5. Kernel validates JWT, creates registry entry with appropriate scope.
|
||||
|
||||
#### Mode B: mTLS
|
||||
|
||||
For production deployments with certificate infrastructure.
|
||||
|
||||
1. Certificate CN encodes identity:
|
||||
Instance: `sidecar:ml-inference`
|
||||
User: `sidecar-user:jeff:local-bridge`
|
||||
2. Kernel's `MTLSNativeProvider` parses the CN prefix and resolves to
|
||||
the appropriate sidecar identity.
|
||||
|
||||
Both modes produce the same internal identity: a `SidecarIdentity`
|
||||
struct with `{package_id, scope, user_id, node_id, registered_at}`.
|
||||
|
||||
### Sidecar Registry
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS sidecar_registry (
|
||||
sidecar_id TEXT PRIMARY KEY,
|
||||
package_id TEXT NOT NULL,
|
||||
scope TEXT NOT NULL DEFAULT 'instance',
|
||||
user_id TEXT,
|
||||
endpoint TEXT NOT NULL,
|
||||
heartbeat TIMESTAMPTZ DEFAULT now(),
|
||||
capabilities JSONB DEFAULT '[]',
|
||||
stats JSONB DEFAULT '{}',
|
||||
registered_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sidecar_pkg ON sidecar_registry (package_id);
|
||||
CREATE INDEX idx_sidecar_user ON sidecar_registry (user_id)
|
||||
WHERE user_id IS NOT NULL;
|
||||
```
|
||||
|
||||
`scope` is `'instance'` or `'user'`. `user_id` is NULL for instance
|
||||
sidecars, set for user sidecars.
|
||||
|
||||
**Lifecycle (mirrors cluster registry):**
|
||||
|
||||
- **Register:** `POST /api/v1/sidecar/register` with capability
|
||||
manifest. Kernel creates/updates registry row.
|
||||
- **Heartbeat:** `POST /api/v1/sidecar/heartbeat` every N seconds.
|
||||
- **Sweep:** Kernel's heartbeat loop sweeps stale sidecar entries
|
||||
alongside node entries. 3× heartbeat interval = stale.
|
||||
- **Self-eviction:** Heartbeat returns 404 → re-register or exit.
|
||||
- **Deregister:** `DELETE /api/v1/sidecar/deregister` on graceful
|
||||
shutdown.
|
||||
|
||||
### Capability Registration
|
||||
|
||||
On registration, the sidecar declares capabilities:
|
||||
|
||||
```json
|
||||
POST /api/v1/sidecar/register
|
||||
{
|
||||
"package_id": "ml-inference",
|
||||
"endpoint": "http://ml-inference:8080",
|
||||
"capabilities": [
|
||||
{
|
||||
"name": "embed",
|
||||
"description": "Generate text embeddings",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": { "type": "string" },
|
||||
"model": { "type": "string", "default": "all-MiniLM-L6-v2" }
|
||||
},
|
||||
"required": ["text"]
|
||||
},
|
||||
"timeout_seconds": 30
|
||||
},
|
||||
{
|
||||
"name": "classify",
|
||||
"description": "Zero-shot text classification",
|
||||
"input_schema": { ... },
|
||||
"timeout_seconds": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Sidecar SDK (Starlark)
|
||||
|
||||
New `sidecar` Starlark module. Permission: `sidecar.call`.
|
||||
|
||||
```python
|
||||
# Call an instance sidecar capability
|
||||
result = sidecar.call("ml-inference", "embed", {"text": "Hello"})
|
||||
|
||||
# Check availability
|
||||
available = sidecar.available("ml-inference")
|
||||
|
||||
# List capabilities
|
||||
caps = sidecar.capabilities("ml-inference")
|
||||
```
|
||||
|
||||
User sidecar calls are routed automatically. When a request originates
|
||||
from user Jeff, `sidecar.call("user-bridge", "fs.search", {...})` hits
|
||||
Jeff's personal sidecar. The same call from user Alice would hit Alice's
|
||||
personal `user-bridge` sidecar (or fail if she doesn't have one). The
|
||||
Starlark caller doesn't specify the user — the kernel resolves it from
|
||||
the request context.
|
||||
|
||||
### Sidecar SDK (Frontend)
|
||||
|
||||
```js
|
||||
const result = await sw.sidecar.call('ml-inference', 'embed', {
|
||||
text: 'Hello, world'
|
||||
});
|
||||
const available = await sw.sidecar.available('user-bridge');
|
||||
```
|
||||
|
||||
Frontend calls go through the kernel API. The kernel proxies to the
|
||||
appropriate sidecar based on scope + user identity.
|
||||
|
||||
### Sidecar API Contract (Inbound to Kernel)
|
||||
|
||||
| Endpoint | Purpose | Auth |
|
||||
|----------|---------|------|
|
||||
| `POST /api/v1/sidecar/register` | Register + declare capabilities | Token or mTLS |
|
||||
| `POST /api/v1/sidecar/heartbeat` | Heartbeat + stats | Token or mTLS |
|
||||
| `DELETE /api/v1/sidecar/deregister` | Graceful shutdown | Token or mTLS |
|
||||
| `GET /api/v1/ext/{pkg}/*` | Read extension data (scoped) | Token or mTLS |
|
||||
| `POST /api/v1/ext/{pkg}/*` | Write extension data (scoped) | Token or mTLS |
|
||||
| `POST /api/v1/sidecar/emit` | Emit events | Token or mTLS |
|
||||
| `GET /api/v1/sidecar/subscribe` | WebSocket event subscription | Token or mTLS |
|
||||
| `POST /api/v1/sidecar/lib/{pkg}/{fn}` | Cross-package function call | Token or mTLS |
|
||||
|
||||
**Scoping rules:**
|
||||
|
||||
- Instance sidecars: can access `/api/v1/ext/{own_package}/*` only.
|
||||
- User sidecars: can access `/api/v1/ext/{own_package}/*` scoped to
|
||||
the owning user's data within that package. The sidecar sees the
|
||||
same data the user would see through the UI.
|
||||
- Event emission scoped to the sidecar's package prefix.
|
||||
|
||||
### Sidecar API Contract (Outbound from Kernel)
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|----------|---------|
|
||||
| `POST {sidecar_endpoint}/api/v1/exec/{capability}` | Execute a capability |
|
||||
| `GET {sidecar_endpoint}/api/v1/health` | Health check |
|
||||
|
||||
The `/exec/{capability}` endpoint receives:
|
||||
|
||||
```json
|
||||
{
|
||||
"input": { ... },
|
||||
"context": {
|
||||
"user_id": "...",
|
||||
"package_id": "...",
|
||||
"request_id": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"output": { ... },
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
What the kernel enforces at the proxy layer:
|
||||
|
||||
- **Timeout:** Per-capability, from capability declaration.
|
||||
- **Request rate:** Per-sidecar, configurable in admin.
|
||||
- **Response size:** Default 10MB, configurable per-sidecar.
|
||||
- **Concurrent calls:** Default 10, configurable per-sidecar.
|
||||
|
||||
User sidecars have separate (typically lower) defaults:
|
||||
|
||||
- **User sidecar rate limit:** Default 30/min (vs 100/min instance).
|
||||
- **User sidecar concurrency:** Default 3 (vs 10 instance).
|
||||
- **User sidecar response size:** Default 5MB (vs 10MB instance).
|
||||
|
||||
Admin can adjust user sidecar defaults globally. Individual users
|
||||
cannot override admin limits.
|
||||
|
||||
### Manifest Integration
|
||||
|
||||
Instance sidecar manifest:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "ml-inference",
|
||||
"type": "full",
|
||||
"tier": "sidecar",
|
||||
"version": "1.0.0",
|
||||
|
||||
"sidecar": {
|
||||
"image": "gobha/armature-ml-inference:latest",
|
||||
"required": true,
|
||||
"health_endpoint": "/api/v1/health",
|
||||
"env_hints": {
|
||||
"ARMATURE_URL": "Kernel URL (auto-populated)",
|
||||
"ARMATURE_TOKEN": "Registration token",
|
||||
"MODEL_PATH": "Path to model files"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
User sidecar manifest (installed as a regular package, sidecar runs
|
||||
on user's machine):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user-bridge",
|
||||
"type": "full",
|
||||
"tier": "sidecar",
|
||||
"version": "1.0.0",
|
||||
|
||||
"sidecar": {
|
||||
"scope": "user",
|
||||
"required": false,
|
||||
"download_url": "https://github.com/armature/user-bridge/releases",
|
||||
"env_hints": {
|
||||
"ARMATURE_URL": "Your Armature instance URL",
|
||||
"ARMATURE_TOKEN": "Generate from Settings → Sidecars"
|
||||
}
|
||||
},
|
||||
|
||||
"surfaces": ["/user-bridge/settings"]
|
||||
}
|
||||
```
|
||||
|
||||
`sidecar.scope: "user"` tells the kernel this is a user sidecar
|
||||
package. The package installs normally (admin or self-install depending
|
||||
on permissions), but the sidecar binary runs on the user's machine.
|
||||
|
||||
### Tool Meta-Tool Integration
|
||||
|
||||
When `llm-bridge` assembles tools for a completion:
|
||||
|
||||
1. Instance-level actions from `sw.actions.list()` → available to all.
|
||||
2. Instance sidecar capabilities → available to all.
|
||||
3. **Requesting user's personal sidecar capabilities** → available to
|
||||
that user only.
|
||||
|
||||
This means different users get different tool sets. Jeff has
|
||||
`user-bridge` running with `fs.search`, `terminal.exec`, and
|
||||
`clipboard.get`. Alice doesn't. When Jeff asks "Hey Max, find the auth
|
||||
file on my desktop," Max has `user-bridge.fs.search` in his tool set.
|
||||
When Alice asks the same, that tool doesn't exist — Max tells her he
|
||||
can't access her local files.
|
||||
|
||||
The same AI persona, different capabilities per user, zero configuration.
|
||||
|
||||
### Admin UI
|
||||
|
||||
**Instance sidecar management:**
|
||||
- Sidecar health status per registered instance sidecar.
|
||||
- Capabilities list with call counts, error rates, latency.
|
||||
- Token management: generate, revoke, list.
|
||||
- Per-sidecar rate limit and concurrency configuration.
|
||||
|
||||
**User sidecar admin controls:**
|
||||
- Permission: `sidecar.connect_personal` — admin grants to specific
|
||||
users or groups.
|
||||
- Capability allowlist for user sidecars — admin restricts which
|
||||
capabilities user sidecars can declare.
|
||||
- Global user sidecar resource limits (rate, concurrency, response
|
||||
size).
|
||||
- Monitoring: list of all connected user sidecars with owner, endpoint,
|
||||
capabilities, last heartbeat.
|
||||
- Kill switch: admin can revoke any user sidecar token or sweep a
|
||||
specific user's sidecars.
|
||||
|
||||
**User settings (for users with `sidecar.connect_personal`):**
|
||||
- Generate personal sidecar token.
|
||||
- List personal connected sidecars with health status.
|
||||
- Revoke own tokens.
|
||||
- Download link / instructions for user sidecar binaries (from package
|
||||
manifest `download_url`).
|
||||
|
||||
### Monitoring
|
||||
|
||||
Kernel-side metrics:
|
||||
|
||||
- `sidecar_call_total{package, capability, scope, status}` — counter.
|
||||
- `sidecar_call_duration_seconds{package, capability, scope}` — histogram.
|
||||
- `sidecar_heartbeat_age_seconds{package, scope}` — gauge.
|
||||
- `sidecar_active{package, scope}` — gauge.
|
||||
- `sidecar_user_count` — gauge (number of connected user sidecars).
|
||||
|
||||
---
|
||||
|
||||
## User Sidecar Use Cases
|
||||
|
||||
### Local Bridge (Desktop Commander Pattern)
|
||||
|
||||
A thin agent on the user's laptop that exposes local capabilities:
|
||||
|
||||
```json
|
||||
{
|
||||
"capabilities": [
|
||||
{
|
||||
"name": "fs.search",
|
||||
"description": "Search local filesystem by filename or content",
|
||||
"input_schema": {
|
||||
"properties": {
|
||||
"query": { "type": "string" },
|
||||
"path": { "type": "string", "default": "~" },
|
||||
"content_search": { "type": "boolean", "default": false }
|
||||
},
|
||||
"required": ["query"]
|
||||
},
|
||||
"timeout_seconds": 15
|
||||
},
|
||||
{
|
||||
"name": "fs.read",
|
||||
"description": "Read a local file",
|
||||
"input_schema": {
|
||||
"properties": {
|
||||
"path": { "type": "string" },
|
||||
"max_bytes": { "type": "integer", "default": 1048576 }
|
||||
},
|
||||
"required": ["path"]
|
||||
},
|
||||
"timeout_seconds": 10
|
||||
},
|
||||
{
|
||||
"name": "terminal.exec",
|
||||
"description": "Execute a shell command",
|
||||
"input_schema": {
|
||||
"properties": {
|
||||
"command": { "type": "string" },
|
||||
"cwd": { "type": "string", "default": "~" },
|
||||
"timeout": { "type": "integer", "default": 30 }
|
||||
},
|
||||
"required": ["command"]
|
||||
},
|
||||
"timeout_seconds": 60
|
||||
},
|
||||
{
|
||||
"name": "clipboard.get",
|
||||
"description": "Read clipboard contents",
|
||||
"input_schema": {},
|
||||
"timeout_seconds": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Tool meta-tool flow:**
|
||||
|
||||
```
|
||||
User: "Hey Max, find the database migration script I was
|
||||
working on yesterday and add it to my notes"
|
||||
|
||||
llm-bridge assembles (for this user):
|
||||
Layer 5 tools: [...instance tools..., user-bridge.fs.search,
|
||||
user-bridge.fs.read, notes.create, ...]
|
||||
|
||||
LLM:
|
||||
1. Tool: user-bridge.fs.search({query: "migration", path: "~/code"})
|
||||
2. Result: [{path: "~/code/armature/migrations/015_sidecar.sql", ...}]
|
||||
3. Tool: user-bridge.fs.read({path: "~/code/armature/migrations/015_sidecar.sql"})
|
||||
4. Result: {content: "CREATE TABLE IF NOT EXISTS sidecar_registry..."}
|
||||
5. Tool: notes.create({title: "Migration 015 - Sidecar Registry",
|
||||
body: "```sql\nCREATE TABLE..."})
|
||||
6. Text: "Found your sidecar migration script and saved it to Notes."
|
||||
```
|
||||
|
||||
Three extensions orchestrated: user's local filesystem, instance-level
|
||||
notes, all through the same tool meta-tool pattern.
|
||||
|
||||
### Personal Compute (Local Ollama)
|
||||
|
||||
User runs Ollama on their workstation with a GPU:
|
||||
|
||||
```json
|
||||
{
|
||||
"capabilities": [
|
||||
{
|
||||
"name": "complete",
|
||||
"description": "LLM completion via local Ollama",
|
||||
"input_schema": {
|
||||
"properties": {
|
||||
"prompt": { "type": "string" },
|
||||
"model": { "type": "string", "default": "llama3" }
|
||||
},
|
||||
"required": ["prompt"]
|
||||
},
|
||||
"timeout_seconds": 120
|
||||
},
|
||||
{
|
||||
"name": "embed",
|
||||
"description": "Local embedding via Ollama",
|
||||
"input_schema": {
|
||||
"properties": {
|
||||
"text": { "type": "string" },
|
||||
"model": { "type": "string", "default": "nomic-embed-text" }
|
||||
},
|
||||
"required": ["text"]
|
||||
},
|
||||
"timeout_seconds": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`llm-bridge` can be configured (per-user setting or persona preference)
|
||||
to route completions through the user's local Ollama instead of the
|
||||
instance-level provider. BYOK taken to the extreme — bring your own
|
||||
hardware.
|
||||
|
||||
### Personal Automation
|
||||
|
||||
A script that reacts to Armature events:
|
||||
|
||||
```python
|
||||
# Personal deploy watcher — connects to Armature, subscribes to
|
||||
# events in the #deploys conversation, runs kubectl locally
|
||||
|
||||
ws = connect(f'{KERNEL_URL}/api/v1/sidecar/subscribe',
|
||||
token=TOKEN)
|
||||
|
||||
for event in ws:
|
||||
if event['type'] == 'chat.message' and 'deploy' in event['content']:
|
||||
result = subprocess.run(['kubectl', 'get', 'pods', '-n', 'prod'],
|
||||
capture_output=True, text=True)
|
||||
# Post result back to chat
|
||||
requests.post(f'{KERNEL_URL}/api/v1/sidecar/lib/chat-core/send',
|
||||
json={'conversation_id': event['conversation_id'],
|
||||
'content': f'```\n{result.stdout}\n```'},
|
||||
headers={'Authorization': f'Bearer {TOKEN}'})
|
||||
```
|
||||
|
||||
Personal workflow glue. No instance-level deployment required.
|
||||
|
||||
---
|
||||
|
||||
## RBAC for User Sidecars
|
||||
|
||||
Three-layer permission model:
|
||||
|
||||
### Layer 1: Admin Controls WHO
|
||||
|
||||
New permission: `sidecar.connect_personal`.
|
||||
|
||||
Admin grants to specific users, groups, or roles. Most users don't
|
||||
need it. Developers, power users, AI-heavy workflows — they get the
|
||||
permission.
|
||||
|
||||
Default: not granted. Opt-in only.
|
||||
|
||||
### Layer 2: Admin Controls WHAT
|
||||
|
||||
**Capability allowlist for user sidecars.**
|
||||
|
||||
Admin setting: `SIDECAR_USER_ALLOWED_CAPABILITIES`
|
||||
|
||||
Default: `["fs.search", "fs.read", "clipboard.get"]` — safe read-only
|
||||
operations.
|
||||
|
||||
To enable terminal access: admin adds `"terminal.exec"` to the
|
||||
allowlist. This is a deliberate escalation requiring admin action.
|
||||
|
||||
A user sidecar that attempts to register a capability not on the
|
||||
allowlist gets a clear error: "Capability 'terminal.exec' not permitted
|
||||
for user sidecars. Contact your administrator."
|
||||
|
||||
### Layer 3: User Controls VISIBILITY
|
||||
|
||||
User sidecar capabilities are **private by default** — only the owning
|
||||
user's requests invoke them.
|
||||
|
||||
Optional: user can share a specific capability with their team via the
|
||||
settings UI. Shared capabilities appear in team members' tool sets
|
||||
(with attribution: "via Jeff's local bridge").
|
||||
|
||||
User sidecars are never instance-wide. That's what instance sidecars
|
||||
are for.
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Instance Sidecars
|
||||
|
||||
1. **Compromised binary** → can read/write own package data, call
|
||||
exported functions. Mitigation: package-scoped access, audit logging,
|
||||
token revocation.
|
||||
2. **Token theft** → impersonate sidecar. Mitigation: package-scoped
|
||||
tokens, mTLS eliminates theft, revocable.
|
||||
3. **SSRF via sidecar.call** → Mitigation: endpoints validated at
|
||||
registration, no runtime changes.
|
||||
4. **Resource exhaustion** → Mitigation: per-sidecar concurrency and
|
||||
rate limits, timeout enforcement, response size limits.
|
||||
5. **Privilege escalation via lib.require** → Mitigation: calls
|
||||
attributed to sidecar identity, target permission checks apply.
|
||||
|
||||
### User Sidecars (Additional Concerns)
|
||||
|
||||
6. **User deploys malicious sidecar** → registers capabilities that
|
||||
exfiltrate data when called. Mitigation: capability allowlist (admin
|
||||
controls what capabilities can be declared), private-by-default
|
||||
(only the user's own requests trigger it — they're exfiltrating
|
||||
from themselves).
|
||||
|
||||
7. **User shares malicious capability with team** → team members
|
||||
invoke it, sidecar captures their request data. Mitigation: shared
|
||||
capabilities are clearly attributed ("via Jeff's local bridge").
|
||||
Admin can disable capability sharing entirely. Shared capabilities
|
||||
run with the CALLING user's permissions, not the sidecar owner's.
|
||||
|
||||
8. **User sidecar as pivot** → user's laptop compromised, attacker uses
|
||||
connected sidecar to access Armature data. Mitigation: user sidecar
|
||||
can only access data the user could already access through the UI.
|
||||
No privilege escalation. User can revoke own tokens. Admin kill
|
||||
switch.
|
||||
|
||||
9. **Stale user sidecars** → user's laptop goes offline, sidecar
|
||||
becomes stale. Mitigation: same sweep mechanism as instance
|
||||
sidecars. 3× heartbeat interval = swept. User reconnects when
|
||||
laptop comes back online.
|
||||
|
||||
### Principle of Least Privilege
|
||||
|
||||
- Instance sidecar: access own package data only, middleware-enforced.
|
||||
- User sidecar: access own package data scoped to owning user only.
|
||||
- Event emission scoped to package prefix.
|
||||
- Capability allowlist for user sidecars (admin-controlled).
|
||||
- Shared capabilities run with caller's permissions.
|
||||
|
||||
---
|
||||
|
||||
## Version Plan
|
||||
|
||||
### v0.14.0 — Sidecar Registry + Auth
|
||||
|
||||
**Goal:** Sidecars can register, authenticate, and heartbeat.
|
||||
|
||||
- Sidecar registry table with `scope` and `user_id` columns.
|
||||
- Registration endpoint with JWT token validation.
|
||||
- Heartbeat + sweep integration.
|
||||
- Token generation (instance tokens via admin, user tokens via settings).
|
||||
- `SIDECAR_AUTH_MODE` config: `token` or `mtls`.
|
||||
- mTLS auth: `sidecar:` and `sidecar-user:` CN prefixes.
|
||||
- Admin UI: sidecar health on packages page.
|
||||
|
||||
**Instance sidecars only in this version.** User sidecar plumbing
|
||||
exists in the schema but user token generation and RBAC gating land
|
||||
in v0.14.5.
|
||||
|
||||
### v0.14.1 — Capability Registration + Execution
|
||||
|
||||
**Goal:** Sidecars declare capabilities. Starlark extensions call them.
|
||||
|
||||
- Capability manifest in registration payload.
|
||||
- `sidecar` Starlark module: `call()`, `available()`, `capabilities()`.
|
||||
- Kernel proxy: `sidecar.call()` → `POST {endpoint}/exec/{cap}`.
|
||||
- Input validation against declared schema.
|
||||
- Timeout, response size, concurrent call limits.
|
||||
- Error propagation.
|
||||
- `sidecar.call` permission.
|
||||
- Audit logging.
|
||||
|
||||
### v0.14.2 — Kernel API Access + Event Bus
|
||||
|
||||
**Goal:** Sidecars read/write their own data and participate in events.
|
||||
|
||||
- Sidecar middleware: authenticate, scope to own package routes.
|
||||
- `/api/v1/sidecar/lib/{pkg}/{fn}` — cross-package function calls.
|
||||
- `/api/v1/sidecar/emit` — event emission (package-scoped).
|
||||
- `/api/v1/sidecar/subscribe` — WebSocket event subscription.
|
||||
- Frontend `sw.sidecar` SDK module.
|
||||
|
||||
### v0.14.3 — Manifest Integration + Admin Polish
|
||||
|
||||
**Goal:** Sidecar packages are first-class in install/admin flow.
|
||||
|
||||
- `tier: "sidecar"` manifest support.
|
||||
- `sidecar` manifest field (image, required, scope, env_hints,
|
||||
download_url).
|
||||
- Admin UI: deployment instructions from manifest.
|
||||
- Admin UI: capability list with metrics.
|
||||
- Admin UI: per-sidecar rate limit configuration.
|
||||
- Token management UI.
|
||||
- Monitoring metrics.
|
||||
|
||||
### v0.14.4 — Reference Sidecar + Instance Quality Gate
|
||||
|
||||
**Goal:** Ship one real instance sidecar. Prove the contract.
|
||||
|
||||
**`armature-embed`** — minimal Python sidecar running
|
||||
sentence-transformers for local embedding generation. One capability:
|
||||
`embed(text) → float[]`.
|
||||
|
||||
`vector-store` calls `sidecar.call('armature-embed', 'embed', {...})`
|
||||
for local embedding without an external API. Completes the local-first
|
||||
RAG story.
|
||||
|
||||
**Instance sidecar quality gate:**
|
||||
|
||||
- Registration, heartbeat, sweep lifecycle tested.
|
||||
- Token and mTLS auth both functional.
|
||||
- Capability execution with limits enforced.
|
||||
- Package-scoped API access enforced.
|
||||
- Event emission/subscription functional.
|
||||
- `sw.sidecar` frontend module functional.
|
||||
- Reference sidecar runs in Docker and K8s.
|
||||
- Admin UI complete.
|
||||
- Monitoring metrics in Grafana.
|
||||
|
||||
### v0.14.5 — User Sidecars
|
||||
|
||||
**Goal:** Users connect personal processes to Armature.
|
||||
|
||||
- `sidecar.connect_personal` permission (admin-granted).
|
||||
- User token generation in Settings → Sidecars.
|
||||
- Capability allowlist for user sidecars (admin setting:
|
||||
`SIDECAR_USER_ALLOWED_CAPABILITIES`).
|
||||
- User sidecar registration with `scope: "user"`, `user_id` binding.
|
||||
- Execution routing: `sidecar.call()` resolves user sidecars based
|
||||
on request context (calling user gets their own sidecar).
|
||||
- Capability sharing: user can share specific capabilities with team.
|
||||
Shared caps attributed ("via Jeff's local bridge").
|
||||
- Separate resource limits for user sidecars (lower defaults).
|
||||
- User settings UI: connected sidecars, token management, sidecar
|
||||
instructions from package manifests.
|
||||
- Admin UI: user sidecar monitoring, kill switch.
|
||||
- Tool meta-tool integration: `llm-bridge` includes user's personal
|
||||
sidecar capabilities in tool set assembly.
|
||||
|
||||
**Reference user sidecar: `user-bridge`** — thin agent binary with
|
||||
`fs.search`, `fs.read`, `clipboard.get` capabilities. Published as a
|
||||
package with `sidecar.scope: "user"`. Binary as a GitHub release.
|
||||
|
||||
**User sidecar quality gate:**
|
||||
|
||||
- RBAC: user without `sidecar.connect_personal` cannot register.
|
||||
- Capability allowlist enforced.
|
||||
- User A cannot invoke user B's sidecar.
|
||||
- Shared capabilities run with caller's permissions.
|
||||
- Admin can revoke any user sidecar.
|
||||
- Tool meta-tool assembles per-user tool sets correctly.
|
||||
- `user-bridge` reference sidecar runs on macOS, Linux, Windows.
|
||||
|
||||
---
|
||||
|
||||
## Schema Summary
|
||||
|
||||
### New tables
|
||||
|
||||
- `sidecar_registry` — sidecar_id, package_id, scope, user_id,
|
||||
endpoint, heartbeat, capabilities (JSON), stats (JSON),
|
||||
registered_at. (v0.14.0)
|
||||
- `sidecar_tokens` — id, package_id, scope, user_id, token_hash,
|
||||
created_by, expires_at, revoked_at. (v0.14.0)
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
| Setting | Default | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `SIDECAR_AUTH_MODE` | `token` | `token` or `mtls` |
|
||||
| `SIDECAR_HEARTBEAT_INTERVAL` | `10s` | Expected heartbeat frequency |
|
||||
| `SIDECAR_STALE_THRESHOLD` | `30s` | 3× heartbeat = stale |
|
||||
| `SIDECAR_MAX_RESPONSE_SIZE` | `10485760` (10MB) | Instance default |
|
||||
| `SIDECAR_DEFAULT_CONCURRENCY` | `10` | Instance default |
|
||||
| `SIDECAR_DEFAULT_RATE_LIMIT` | `100/min` | Instance default |
|
||||
| `SIDECAR_USER_MAX_RESPONSE_SIZE` | `5242880` (5MB) | User default |
|
||||
| `SIDECAR_USER_CONCURRENCY` | `3` | User default |
|
||||
| `SIDECAR_USER_RATE_LIMIT` | `30/min` | User default |
|
||||
| `SIDECAR_USER_ALLOWED_CAPABILITIES` | `["fs.search","fs.read","clipboard.get"]` | Admin-controlled allowlist |
|
||||
|
||||
---
|
||||
|
||||
## Sidecar Author Contract
|
||||
|
||||
### Minimum Viable Sidecar (Python)
|
||||
|
||||
```python
|
||||
from flask import Flask, request, jsonify
|
||||
import requests, os, threading, time
|
||||
|
||||
app = Flask(__name__)
|
||||
KERNEL = os.environ['ARMATURE_URL']
|
||||
TOKEN = os.environ['ARMATURE_TOKEN']
|
||||
|
||||
def register():
|
||||
requests.post(f'{KERNEL}/api/v1/sidecar/register',
|
||||
json={
|
||||
'package_id': 'my-sidecar',
|
||||
'endpoint': f'http://localhost:8080',
|
||||
'capabilities': [{
|
||||
'name': 'echo',
|
||||
'description': 'Echo input back',
|
||||
'input_schema': {
|
||||
'type': 'object',
|
||||
'properties': {'text': {'type': 'string'}},
|
||||
'required': ['text']
|
||||
},
|
||||
'timeout_seconds': 10
|
||||
}]
|
||||
},
|
||||
headers={'Authorization': f'Bearer {TOKEN}'})
|
||||
|
||||
def heartbeat():
|
||||
while True:
|
||||
time.sleep(10)
|
||||
r = requests.post(f'{KERNEL}/api/v1/sidecar/heartbeat',
|
||||
json={'stats': {}},
|
||||
headers={'Authorization': f'Bearer {TOKEN}'})
|
||||
if r.status_code == 404:
|
||||
register()
|
||||
|
||||
@app.route('/api/v1/exec/echo', methods=['POST'])
|
||||
def exec_echo():
|
||||
return jsonify({
|
||||
'output': {'text': request.json['input']['text']},
|
||||
'error': None
|
||||
})
|
||||
|
||||
@app.route('/api/v1/health')
|
||||
def health():
|
||||
return jsonify({'status': 'ok'})
|
||||
|
||||
register()
|
||||
threading.Thread(target=heartbeat, daemon=True).start()
|
||||
app.run(port=8080)
|
||||
```
|
||||
|
||||
Four things: register, heartbeat, `/exec/`, `/health`. That's the
|
||||
contract.
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| Connect-inward | Eliminates K8s RBAC, service mesh, DNS discovery. Works on any deployment target. |
|
||||
| Two auth modes | Token for simple. mTLS for production. Same internal identity. |
|
||||
| Cluster registry pattern | Proven heartbeat/sweep. No new coordination mechanism. |
|
||||
| HTTP/JSON, no gRPC | KISS. 4-endpoint contract. Every language has HTTP. |
|
||||
| Kernel proxies all calls | Auth, rate limits, audit, timeouts at single control point. |
|
||||
| Package-scoped access | Compromised sidecar can't read other packages. |
|
||||
| User sidecars as v0.14.5 | Prove instance contract first (v0.14.0–v0.14.4), then extend to users. Same mechanism, new scope. |
|
||||
| Three-layer user RBAC | Admin controls who (permission), what (capability allowlist), user controls visibility (private/shared). Defense in depth. |
|
||||
| Capability allowlist for user sidecars | Admin decides what user processes can expose. `terminal.exec` is a deliberate escalation. |
|
||||
| Private-by-default user capabilities | User sidecar can't affect other users unless explicitly shared. |
|
||||
| Shared caps run with caller's perms | Prevents privilege escalation through shared capabilities. |
|
||||
| Reference instance sidecar: embedding | Simpler than LLM. Proves contract. Completes local RAG story. |
|
||||
| Reference user sidecar: file bridge | Most compelling user sidecar demo. "Find a file on my laptop and save it to notes" is visceral. |
|
||||
| Lower resource limits for user sidecars | User sidecars are less trusted (running on personal devices). Lower defaults, admin can't be overridden by users. |
|
||||
| Separate from polish (v0.15.x) | Sidecar is security-critical infrastructure. Polish is quality-of-life. Don't mix. |
|
||||
|
||||
---
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **Streaming responses.** LLM sidecar needs token streaming. SSE or
|
||||
chunked transfer. Deferred — batch covers embedding, classification,
|
||||
transcoding.
|
||||
- **SDK libraries.** Thin wrappers in Python, Go, Node, Rust for
|
||||
registration + heartbeat + parsing. Convenience, not requirement.
|
||||
- **Auto-scaling.** Multiple instances of same sidecar, kernel
|
||||
load-balances. No schema change needed — registry supports multiple
|
||||
rows per package.
|
||||
- **GPU detection.** Kernel capability for manifest negotiation.
|
||||
- **Sidecar-to-sidecar.** Direct calls for performance pipelines.
|
||||
Currently goes through kernel.
|
||||
- **User sidecar desktop app.** Electron/Tauri tray app that wraps the
|
||||
user-bridge agent with a GUI for capability management and connection
|
||||
status. Post-1.0.
|
||||
157
docs/ROADMAP-v010x-v100.md
Normal file
157
docs/ROADMAP-v010x-v100.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# ROADMAP — v0.10.x → v1.0.0 (Final)
|
||||
|
||||
## Full Roadmap
|
||||
|
||||
| Series | Title | Versions | Design Doc |
|
||||
|--------|-------|----------|------------|
|
||||
| v0.9.x | Multi-Surface + Workflow Redesign | 10 | — |
|
||||
| **v0.10.x** | **Panels + Composable Layout** | 5 | `DESIGN-panels.md` |
|
||||
| **v0.11.x** | **Notes Reference Extension** | 11 | `DESIGN-notes-v011x.md` |
|
||||
| **v0.12.x** | **Chat Reference Extension** | 9 | `DESIGN-chat-v012x.md` |
|
||||
| **v0.13.x** | **Reference Libraries + Extensions** | 9 | — |
|
||||
| **v0.14.x** | **Sidecar Tier** | 6 | `DESIGN-sidecar-v014x.md` |
|
||||
| **v0.15.x** | **Polish + Stability** | 3 | — |
|
||||
| **v1.0.0** | **Stable Release** | — | — |
|
||||
|
||||
**Total: ~53 versions from v0.10.0 to v1.0.0**
|
||||
|
||||
---
|
||||
|
||||
## v0.10.x — Panels + Composable Layout
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.10.0 | Panel Manifest + Lifecycle |
|
||||
| v0.10.1 | FloatingPanel Primitive |
|
||||
| v0.10.2 | Docked Panels + Mode Transitions |
|
||||
| v0.10.3 | Panel Communication Patterns |
|
||||
| v0.10.4 | Reference Panel: Notes (basic) |
|
||||
|
||||
---
|
||||
|
||||
## v0.11.x — Notes Reference Extension
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.11.0 | UI/UX Foundation |
|
||||
| v0.11.1 | Deep Folders + Navigation |
|
||||
| v0.11.2 | Wikilinks + Backlinks |
|
||||
| v0.11.3 | Live Preview + Rich Editing |
|
||||
| v0.11.4 | Note Sharing + Permissions |
|
||||
| v0.11.5 | Graph + Outline Hardening |
|
||||
| v0.11.6 | Quick Switcher + Commands |
|
||||
| v0.11.7 | Daily Notes + Templates |
|
||||
| v0.11.8 | Transclusion + Embeds |
|
||||
| v0.11.9 | Composability: Slots + Actions |
|
||||
| v0.11.10 | Panel Enhancement + Quality Gate |
|
||||
|
||||
---
|
||||
|
||||
## v0.12.x — Chat Reference Extension
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.12.0 | UI/UX Foundation |
|
||||
| v0.12.1 | Conversation Folders + Attributes |
|
||||
| v0.12.2 | Reactions + Threads + Pins |
|
||||
| v0.12.3 | Rich Compose + Attachments |
|
||||
| v0.12.4 | @Mentions + Notifications |
|
||||
| v0.12.5 | Link Previews + Message Formatting |
|
||||
| v0.12.6 | Conversation Themes + Personality |
|
||||
| v0.12.7 | Composability: Slots + Actions |
|
||||
| v0.12.8 | Panels + Quality Gate |
|
||||
|
||||
---
|
||||
|
||||
## v0.13.x — Reference Libraries + Extensions
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.13.0 | `vector-store` Library |
|
||||
| v0.13.1 | `llm-bridge` Core Library |
|
||||
| v0.13.2 | `llm-bridge` → Chat: Multi-Persona + Tool Meta-Tool |
|
||||
| v0.13.3 | `llm-bridge` → Notes: AI Toolbar Actions |
|
||||
| v0.13.4 | `file-share` Extension |
|
||||
| v0.13.5 | `code-workspace` Extension |
|
||||
| v0.13.6 | `image-gen` + `image-edit` Extensions |
|
||||
| v0.13.7 | Tool Meta-Tool Hardening + Scoping |
|
||||
| v0.13.8 | Integration Quality Gate |
|
||||
|
||||
---
|
||||
|
||||
## v0.14.x — Sidecar Tier
|
||||
|
||||
Connect-inward model. Instance sidecars (shared infrastructure) and
|
||||
user sidecars (personal local tools).
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.14.0 | Sidecar Registry + Auth |
|
||||
| v0.14.1 | Capability Registration + Execution |
|
||||
| v0.14.2 | Kernel API Access + Event Bus |
|
||||
| v0.14.3 | Manifest Integration + Admin Polish |
|
||||
| v0.14.4 | Reference Sidecar (`armature-embed`) + Instance Gate |
|
||||
| v0.14.5 | User Sidecars + Reference (`user-bridge`) + User Gate |
|
||||
|
||||
---
|
||||
|
||||
## v0.15.x — Polish + Stability
|
||||
|
||||
| Version | Title |
|
||||
|---------|-------|
|
||||
| v0.15.0 | Native Dialog Audit |
|
||||
| v0.15.1 | Versioned Migrations + `armature migrate` CLI |
|
||||
| v0.15.2 | Pre-1.0 Schema Freeze + Upgrade Path Validation |
|
||||
|
||||
---
|
||||
|
||||
## v1.0.0 Gate Criteria
|
||||
|
||||
**Reference extensions:**
|
||||
- Notes shipped (all v0.11.x) with UI quality review
|
||||
- Chat shipped (all v0.12.x) with UI quality review
|
||||
- At least 3 reference extensions beyond notes/chat
|
||||
|
||||
**Composability:**
|
||||
- `llm-bridge` extends both notes and chat through slots/actions
|
||||
- Tool meta-tool: AI uses 3+ extension actions in a single turn
|
||||
- At least 2 panels consumed cross-package
|
||||
- At least 2 slot contributions per host surface
|
||||
|
||||
**Sidecar:**
|
||||
- Instance sidecar (`armature-embed`) deployed and functional
|
||||
- User sidecar (`user-bridge`) functional on macOS/Linux/Windows
|
||||
- Token + mTLS auth both tested
|
||||
- User sidecar RBAC enforced (permission, capability allowlist, scoping)
|
||||
- Tool meta-tool includes user sidecar capabilities per-user
|
||||
|
||||
**Infrastructure:**
|
||||
- Admin safety rails validated
|
||||
- Multi-persona context archetypes demonstrated
|
||||
- Sharing functional (notes + conversations)
|
||||
- Headless E2E green on PG + SQLite
|
||||
- All kernel Starlark modules documented
|
||||
- All API routes covered by OpenAPI spec
|
||||
- `armature migrate` CLI functional
|
||||
- Upgrade path tested v0.8.0 → v1.0.0
|
||||
- Single-binary + Docker + K8s deployment paths documented
|
||||
- Monitoring dashboard with kernel + sidecar metrics
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions Log
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| Panels as kernel primitive | Z-index, drag, layout are kernel concerns |
|
||||
| UI/UX-first for reference extensions | Every feature builds on visual foundation |
|
||||
| Notes → Chat → Libraries → Sidecar | Each builds on proven patterns from previous |
|
||||
| Chat human-to-human first | AI is llm-bridge's job |
|
||||
| Folder attributes as extension bridge | Zero coupling between chat and llm-bridge |
|
||||
| Action registry as tool registry | Installed extensions = AI capabilities |
|
||||
| 6-layer prompt architecture | Admin safety not overridable |
|
||||
| Sidecar connect-inward | No K8s RBAC, no service mesh, no DNS discovery |
|
||||
| Sidecar HTTP/JSON, not gRPC | KISS. 4-endpoint contract |
|
||||
| User sidecars in v0.14.5 | Prove instance contract first, extend to users with same mechanism |
|
||||
| Three-layer user sidecar RBAC | Admin controls who + what, user controls visibility |
|
||||
| Separate polish (v0.15.x) | Security-critical infra and quality-of-life shouldn't share focus |
|
||||
Reference in New Issue
Block a user