Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a1b216a
docs(plans): member machines — the workspace is a server
pythonlearner1025 Aug 28, 2026
463c09d
docs(plans): record the ground-truth survey for member machines
pythonlearner1025 Aug 28, 2026
fd7ac56
docs(plans): member machines v2 — workspace is its own template
pythonlearner1025 Aug 28, 2026
b0a06e4
docs(plans): machine types are per machine, never a workspace restric…
pythonlearner1025 Aug 28, 2026
298f5fa
docs(plans): sidecar the strip+rail UI refactor, sessions-free
pythonlearner1025 Aug 28, 2026
bf6b93f
docs(plans): ground the UI sidecar in the session-rail mockup
pythonlearner1025 Aug 28, 2026
13b13bb
docs(plans): drop the invite concept from the workspace model
pythonlearner1025 Aug 28, 2026
222a4e7
docs(plans): unify boxes into machines — one row, no stored principal
pythonlearner1025 Aug 28, 2026
a4fe982
docs(plans): workspace admin owns workspace credentials; member/type UI
pythonlearner1025 Aug 28, 2026
e515c96
docs(plans): rename workspace role editor to member; creation stays o…
pythonlearner1025 Aug 28, 2026
82bf922
docs(plans): explain the two workspace-credential deliveries
pythonlearner1025 Aug 28, 2026
fc8e26d
docs(plans): unify workspace credentials — one sealed store, two reads
pythonlearner1025 Aug 28, 2026
45c4d95
docs(plans): workspace credentials serve through blitz-cred only
pythonlearner1025 Aug 28, 2026
8c62c51
broker: delete the workspace-environment fetch and the env.d writer
claude Aug 28, 2026
38ca25a
box: stop sourcing creds/env.d in blitz-term and in chat turns
claude Aug 28, 2026
51becac
webapp: split CloudApp into rail, panes, routes and dialog stack
claude Aug 28, 2026
91b2b50
schema: retire the workspace-environment cross-runtime contract
claude Aug 28, 2026
f8fc34e
webapp: query the shell by role and label, not by class
claude Aug 28, 2026
900ca00
box: drop the last creds/env.d references from guest comments
claude Aug 28, 2026
042635a
webapp: build the strip and the session rail, retire DriveRail
claude Aug 29, 2026
fac9d2b
webapp: delete the environment and startup-script surface
claude Aug 29, 2026
4d6c551
feat(control-plane): the workspace is a server, every member gets a m…
pythonlearner1025 Aug 29, 2026
6a35d27
chore(control-plane): re-point the module graph, blitzdev schema and …
pythonlearner1025 Aug 29, 2026
e859af1
test(control-plane): re-point the suite's seeding and SQL at machines
pythonlearner1025 Aug 29, 2026
90de444
test(control-plane): machines in graceful destroy, box config, identi…
pythonlearner1025 Aug 29, 2026
9a13758
test(control-plane): pin the legacy environment shim and the member-r…
pythonlearner1025 Aug 29, 2026
4e2e802
test(control-plane): rewrite the template suite around workspace clones
pythonlearner1025 Aug 29, 2026
a92db57
test(control-plane): recipes launch from a workspace and its launch i…
pythonlearner1025 Aug 29, 2026
3a8bebf
feat(control-plane): unmount the template and recipe surfaces
pythonlearner1025 Aug 29, 2026
8f0601a
test(control-plane): member routes replace grants; the viewer reaches…
pythonlearner1025 Aug 29, 2026
8e64ab6
docs: record the member-machines contracts and the machine-based vmsUsed
pythonlearner1025 Aug 29, 2026
36a96fa
test(control-plane): pin the managed module manifest for the new core…
pythonlearner1025 Aug 29, 2026
293f4e3
chore(control-plane): keep providerOperationError honest about its un…
pythonlearner1025 Aug 29, 2026
c636430
Merge branch 'feat/mm-guest' into feat/member-machines-int
pythonlearner1025 Aug 29, 2026
50355e4
Merge branch 'feat/mm-webapp' into feat/member-machines-int
pythonlearner1025 Aug 29, 2026
a681b24
Merge remote-tracking branch 'origin/main' into feat/member-machines-int
pythonlearner1025 Aug 29, 2026
a992476
wire: the member-machines fields are required, not optional
pythonlearner1025 Aug 29, 2026
30fc087
webapp: build the member, machine and credential surfaces against the…
pythonlearner1025 Aug 29, 2026
ec46063
webapp: disable the template and recipe routes; cover the new create …
pythonlearner1025 Aug 29, 2026
1846c98
webapp: drop the org-share read path with its route
pythonlearner1025 Aug 29, 2026
0fa9ef0
webapp: offer a machine action only where the wire gives an id to act on
pythonlearner1025 Aug 29, 2026
f4f2086
workspaces: make the settings a workspace admin owns writable
pythonlearner1025 Aug 29, 2026
c1bd5ef
workspaces: give a machineless member a way to get a machine
pythonlearner1025 Aug 29, 2026
4495b93
Delete the retired create-workspace surfaces and their dead chains
pythonlearner1025 Aug 29, 2026
37a0783
Project a workspace record through one helper, so agentRuleId survive…
pythonlearner1025 Aug 29, 2026
fa3e6fb
docs: rewrite the README for member machines
pythonlearner1025 Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,71 @@ conformance tests on BOTH sides. Never hand-edit one side of a contract.
| MICROVM_HOSTS | runtime + deploy share ONE parser | n/a (shared code) | `core/compute/microvm-hosts.js` imported by both |
| dufs WebDAV listing | `core/files/sync.ts` parser ↔ dufs in the box image | `fixtures/dav-listing/` | `test/dav-listing-fixtures.test.ts` (TS side; guest side revalidates at box-image rebuild) |
| public preview links | box CLI state ↔ Go gateway ↔ browser | `fixtures/previews/` | `gateway/main_test.go`, `webapp/test/preview-v2.test.ts` |
| workspace environment | `core/environment.ts` route ↔ `broker/internal/workspace/environment.go` ↔ `box/actor/src/credentials.ts` (`env` only) | `fixtures/workspace-environment/` | `test/workspace-environment-conformance.test.ts`, `broker` `environment_test.go`, `actor/test/workspace-environment.test.ts` |
| preview-focus | `blitz teenyapp open` CLI (`blitz preview` stays a silent alias; wire unchanged) ↔ Go gateway (`/preview-focus`) ↔ browser (`webapp/src/preview.ts` consumer, auto-opens the focus) | `fixtures/preview-focus/` | `box/actor/test/preview-focus-conformance.test.ts` (producer), `gateway/main_test.go` (reader), `webapp/test/preview-focus.test.ts` (browser consumer) |
| connections-focus | `blitz connections open <provider>` CLI ↔ Go gateway (`/connections-focus`) ↔ browser (`webapp/src/connections-focus.ts` consumer via `use-workspace-connections-focus.ts`, opens the workspace connections panel with the provider selected) | `fixtures/connections-focus/` | `box/actor/test/connections-focus-conformance.test.ts` (producer), `gateway/main_test.go` (reader), `webapp/test/connections-focus.test.ts` (browser consumer) |
| webApp ticket v1 | `core/webapp-tickets.ts` mint/verify ↔ `box/gateway/main.go` ↔ `box/actor/src/auth.ts` | `fixtures/webapp-ticket/` | `test/webapp-ticket-conformance.test.ts`, `gateway/main_test.go` (ticket_conformance_test.go), `actor/test/auth-conformance.test.ts` |
| schema ↔ wire copy | `packages/schema/src` ↔ `control-plane/core/wire.ts` | n/a | `test/wire-drift.test.ts` (full field coverage) |
| microVM agent protocol | `microvm-host/types.go` ↔ `core/compute/microvm-agent.ts` | none yet — add fixtures before changing either side | — |
| webApp box surface | `core/webapp-surface.ts` ↔ `schema/src/webapp-surface.ts` (webApp resolver) | n/a | `test/webapp-surface-drift.test.ts`, `webapp/test/webapp-surface.test.ts` |
| agent rules | CP `core/agent-rules.ts` producer (`GET /workspaces/self/agent-rules`) ↔ box `blitz-rules sync` consumer (`box/rootfs/usr/local/bin/blitz-rules`); `AGENT_RULES_DOC` mirrors the canonical `box/rootfs/opt/blitz/skel/agent-rules.md` | `fixtures/agent-rules/` | `test/agent-rules-conformance.test.ts` + `test/agent-rules-drift.test.ts` (CP), `box/actor/test/agent-rules-conformance.test.ts` (box) |
| connection pull v1 | CP producer `core/connections/pull-wire.ts` (`GET /workspaces/self/connections`, `POST /workspaces/self/connections/:name/token`) ↔ Go consumer `broker/internal/workspace/connections.go`, printed by `blitz-cred list\|get\|env` | `fixtures/connection-pull/` | `test/connection-pull-conformance.test.ts` + `test/pull-credentials.test.ts` (CP), `broker/internal/workspace/connections_test.go` + `broker/cmd/blitz-cred/main_test.go` (box) |
| connection pull v1 | CP producer `core/connections/pull-wire.ts`, routes in `core/connections/pull-routes.ts` (`GET /workspaces/self/connections`, `POST /workspaces/self/connections/:name/token`) ↔ Go consumer `broker/internal/workspace/connections.go`, printed by `blitz-cred list\|get\|env`. Carries BOTH credential planes: the member's own connection grant and the workspace credential store (plans/MEMBER-MACHINES.md §4) | `fixtures/connection-pull/` | `test/connection-pull-conformance.test.ts` + `test/pull-credentials.test.ts` + `test/member-machines.test.ts` (CP), `broker/internal/workspace/connections_test.go` + `broker/cmd/blitz-cred/main_test.go` (box) |
| entitlements | CP `core/entitlements.ts` (`PUT /orgs/:id/entitlements` writer, `GET /orgs/:id/usage`, the 402 seat-limit refusal and its HS256 handoff token) ↔ the PRIVATE billing service, which owns plans, writes the integers, and verifies the token — core never learns a plan name | `fixtures/entitlements/` | `test/entitlements-fixtures.test.ts` (CP); the billing service copies the corpus and pins it on its side |
| recipe invocation files | `core/bootstrap.ts` writer (recipe launches emit `/var/lib/blitz/recipe/prompt.txt` + `invocation.env`) ↔ guest readers: `blitz-term` through the shared parser `box/rootfs/usr/local/libexec/blitz-recipe-invocation`, plus the bootstrap-emitted chat sender's raw `prompt.txt` read (the sender never parses `invocation.env` — model/effort/permission are interpolated into its source at render time) | `fixtures/recipe-invocation/` | `test/recipe-invocation-fixtures.test.ts` (CP), `box/actor/test/recipe-invocation-guest.test.ts` (guest: shared parser vs corpus + blitz-term delivery semantics) |
| box config v1 | CP `core/box-config.ts` producer (`GET /workspaces/self/box-config`) and consumer (`POST /workspaces/self/box-update-result`) ↔ host updater bash/python emitted by `core/bootstrap.ts` (`blitz-box-update`; cloud-VM path only — the microVM provider has its own guest lifecycle and no update path yet) | `fixtures/box-config/` | `test/box-config-conformance.test.ts` (CP), `test/box-update-conformance.test.mjs` (runs real `python3` over the emitted parser/producer, `bash -n` over the emitted scripts), `test/box-update-host.test.mjs` (runs the emitted updater in real bash against a live CP over real curl) |

Retired: the `workspace environment` contract (`GET /workspaces/self/environment`
→ `creds/env.d/00-workspace.sh`). Workspace credentials serve through
`blitz-cred` alone — see `plans/MEMBER-MACHINES.md` §1. The route stays only for
box images already in the field, which tolerate an empty answer.

Legacy phone-home shapes are accepted ONLY inside
`adaptLegacyPhoneHomeRequestForInFlightImages` in `core/workspaces.ts`.
Do not add aliases anywhere else.

## Member machines: what a workspace is now

`plans/MEMBER-MACHINES.md` landed in migrations 0041-0044. Two invariants an
agent must not undo:

- **A workspace is configuration; a `machines` row is the VM.** The workspace
has no phase, no `vm_id`, and no environment. `WorkspaceView.phase` and
`.ssh` survive as a projection of the REQUESTING member's machine, so old
pollers keep converging — do not start storing them again.
- **The acting principal is resolved at call time** from
`machines.membership_id` (`core/oauth.ts`, `core/connections/mint.ts`).
Nothing about who a guest acts as is stored beside its credential. The row
that used to hold it pinned the workspace owner, and that is the bug the
structure now prevents.

Three compatibility surfaces are load-bearing and have no expiry date yet:
`GET /boxes/:id/feed` (served from `machines`),
`GET /workspaces/:id/environment` (a constant `{env:{}, startupScript:null,
filesReady:true}`, because deployed brokers poll it every second at boot and
wait for exactly those three fields), and the token families migration 0041
copied hash-for-hash so no deployed guest had to re-enrol.

The `workspace environment` cross-runtime contract is retired with its fixtures
and both conformance tests: no runtime reads the route any more, so what remains
is that constant three-field shim, pinned alone by
`control-plane/test/workspace-environment.test.ts`.

Every field of `WorkspaceView` is required, including `members`,
`credentials`, `myRole`, `defaultMachineTypeId` and `autoProvision`. The only
client is `packages/webapp`, built from `packages/schema` in this tree, so a
server that drops a field fails `test/wire-drift.test.ts` rather than the
browser. Do not make one optional to spare a fixture.

Templates and Recipes are disabled product-wide (2026-08-29), on both sides:

- Control plane: the two registrations are commented out in `core/app.ts`.
- Webapp: the `/templates*` and `/recipes*` branches are commented out in
`sessions-page-state.ts`, so those addresses fall through to Drive, and
`shell/SecondaryRoutes.tsx` no longer renders them.

The page components (`TemplatesHome`, `RecipesHome`, `CreateTemplateScreen`,
`CreateRecipeScreen`), the client methods and the recipe rows are untouched
and unreachable. Restoring a surface means restoring both branches.

## VM provider architecture (do not regress)

- The plugin contract is `VmProvider` in `core/compute/types.ts`:
Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<p align="center">
<a href="#features">Features</a> •
<a href="#architecture">Architecture</a> •
<a href="#about">About</a> •
<a href="#packages">Packages</a>
</p>

Expand All @@ -14,22 +14,23 @@

# Features

**BYO agent and run them on any cloud**
**BYO agent and run them on any cloud**

- **Agent workspaces** Shareable, sandboxed cloud computers holding only the credentials and data AI agents need.
- **Workspace templates** Create agent workspace templates defining what repos, credentials, machine environment, etc. are put in the agent workspace. Set up once and share with everyone.
- **Recipes** Like skills but on steroids: define the runtime of a successful agent workflow (AI model, machine env, data + credentials) and trigger it with a webhook from Slack, GitHub, etc.
- **A machine for every member** Join a workspace like you join a Discord server: your own machine is already running, preloaded with the team's repos, rules, and keys. Nobody files a ticket for a VM.
- **Workspace credentials** Sealed, workspace-scoped API keys that agents fetch at the moment of use with `blitz-cred`. Nothing ambient, nothing in a dotfile, revocation that actually revokes.
- **Everyone acts as themselves** Your GitHub and your agent subscriptions stay yours on your machine. Commits carry your name, tokens spend your identity — even in a workspace the whole team shares.
- **Teenyapps** Mini apps you can vibe-code to build internal tools like dashboards, CRMs, and task managers — each comes with a backend, auth, and a URL.

# About

An operating system turns a bare machine into a standard environment where programs just run. BlitzOS does the same for AI: it turns the context, credentials, and workflows your fast adopters have dialed in into reusable, standardized workspaces that the rest of the company can just use, without learning the agent setup themselves.

When a new AI agent drops, a developer tests it in a fresh "daily driver" workspace from a company template. That template preconfigures the repos, API keys, and agent skill library, and nothing else. The clean boundary keeps the agent safe to run on real work in the cloud, unlike a local machine where every other project's credentials and configs are reachable.
When a new AI agent drops, a developer tests it on their own machine inside the team workspace. The workspace preconfigures the repos, API keys, and agent rules, and nothing else. The clean boundary keeps the agent safe to run on real work in the cloud, unlike a local machine where every other project's credentials and configs are reachable.

If the agent does something interesting, developers share the workspace like a Google Doc. If it's genuinely better but needs config tweaks, one developer publishes an updated template and everyone rolls it out.
If the setup is genuinely better, spreading it is one action: add a teammate, and a machine with the same repos, rules, and keys is theirs before their first click. A workspace **is** its own template — when a configuration is worth keeping, clone the workspace and the next team starts where this one left off.

BlitzOS helps companies digest AI advancements as fast as they happen. Workspaces, templates, and recipes enable **faster experimentation** and **workflow sharing**, and features like automatic evals build on them to optimize AI cost.
BlitzOS helps companies digest AI advancements as fast as they happen. Workspaces and member machines enable **faster experimentation** and **workflow sharing**, and features like automatic evals build on them to optimize AI cost.

# Installation

Expand All @@ -49,7 +50,7 @@ Follow the [self-host guide](docs/SELF-HOST.md).
# Packages

- [`box`](packages/box/README.md) the complete workspace runtime: SSH, Docker, agent harnesses, terminal, chat, files, and previews.
- [`control-plane`](packages/control-plane/README.md) workspace lifecycle, sessions, access, credential injection, volumes, and compute providers.
- [`control-plane`](packages/control-plane/README.md) workspaces, member machines, roles, credential injection, volumes, and compute providers.
- [`microvm-host`](packages/microvm-host/README.md) the Go host agent that runs and networks Firecracker workspaces.
- [`webApp`](packages/webapp/README.md) the browser webApp for creating, configuring, sharing, and working inside workspaces.
- [`broker`](packages/broker/README.md) short-lived Claude and Codex credential delivery for workspace fleets.
Expand All @@ -68,6 +69,7 @@ Follow the [self-host guide](docs/SELF-HOST.md).

# Roadmap

- [ ] sessions
- [ ] recipes
- [ ] evals
- [ ] policy
Expand Down
11 changes: 3 additions & 8 deletions packages/box/actor/src/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,6 @@ class SessionActor {
stopReason = "cancelled";
return { stopReason };
}
// Workspace variables are optional configuration: this call degrades to
// the actor's own environment rather than failing the prompt.
const environment = await this.credentials.environment();
if (abort.signal.aborted) {
stopReason = "cancelled";
return { stopReason };
}
try {
const output = await this.adapter.runTurn({
sessionId: this.id,
Expand All @@ -194,7 +187,9 @@ class SessionActor {
resumeId: this.resumeId,
signal: abort.signal,
token,
environment,
// The actor's own environment, and nothing added. Every credential a
// turn may use is pulled at the moment of use with `blitz-cred get`.
environment: process.env,
config: this.config,
emit: (update) => this.emit(update, identity),
requestPermission: (request) => this.requestPermission(request, identity),
Expand Down
Loading
Loading