Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
25bbb71
feat: add the control plane, policy engine, scheduler and stores
reachjalil Oct 28, 2026
483ad93
feat: add the Firecracker node agent and in-guest agent
reachjalil Oct 28, 2026
1a23131
feat: add the differently CLI and Go client
reachjalil Oct 28, 2026
d8956ca
feat: add TypeScript and Python SDKs
reachjalil Oct 28, 2026
8430678
feat: add Helm chart, Terraform, systemd units and image builds
reachjalil Oct 28, 2026
017bdb7
docs: add architecture, decisions, guides and feature parity
reachjalil Oct 28, 2026
11d52a9
ci: add CI, release and dependency automation
reachjalil Oct 28, 2026
c0bdb78
ci: add code scanning and back-merge main after releases
reachjalil Oct 28, 2026
d3f1d33
feat(site): add Astro website and Starlight docs for GitHub Pages (#2)
reachjalil Oct 28, 2026
5f229ef
fix(security): pin agent identity, verify VM PIDs, add fail-closed au…
reachjalil Oct 28, 2026
2fd8518
feat(site): lead the landing page with orchestration and resource man…
reachjalil Oct 28, 2026
8a6187c
test(e2e): run the real stack on Firecracker microVMs and fix what it…
reachjalil Oct 28, 2026
c2617d3
feat(adapters): add an OpenSandbox backend, checked against a live se…
reachjalil Oct 28, 2026
cc1947c
refactor: run sandboxes on OpenSandbox instead of Differently's own n…
reachjalil Oct 28, 2026
9d931a3
fix(cli): print every error instead of exiting silently (#7)
reachjalil Oct 28, 2026
a738c93
feat(site): add a features section comparing OpenSandbox, Differently…
reachjalil Oct 28, 2026
151a320
feat: serve OpenWork's sandbox contract, routing one image name to ev…
reachjalil Oct 28, 2026
e4faa80
feat: manage provider keys and API keys in Differently, and switch pr…
reachjalil Oct 28, 2026
5316042
docs(openwork): note how OpenSandbox's Kubernetes runtime affects Ope…
reachjalil Oct 28, 2026
2b86a24
chore(release): prepare v0.1.0-alpha.1 (#15)
reachjalil Oct 28, 2026
395c639
fix(opensandbox): handle what real runtimes return, replayed in go te…
reachjalil Oct 29, 2026
c079e50
fix(security): reject unsafe devhost refs and serve sandbox files as …
reachjalil Oct 29, 2026
a6c3651
fix(sdk): trim trailing slashes without a backtracking regex (#17)
reachjalil Oct 29, 2026
4e3c8e3
ci: approve pull requests once CI passes (#18)
reachjalil Oct 29, 2026
d27be0d
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
f8ab6c2
docs: guide agents through maintaining and improving Differently (#19)
reachjalil Oct 29, 2026
5ebc95f
docs: make Differently easy to evaluate and deploy anywhere (#21)
reachjalil Oct 29, 2026
dc384e0
feat(opensandbox): bring OpenSandbox pools close to Freestyle (#20)
reachjalil Oct 29, 2026
9311b84
feat: stop idle sandboxes and wake them on the next call (#22)
reachjalil Oct 29, 2026
4e3b0c0
docs: add a provider catalog with a tested status for each backend (#23)
reachjalil Oct 29, 2026
f480dbe
feat(adapters): add Vercel Sandbox and Fly.io Machines providers (#24)
reachjalil Oct 29, 2026
3b55002
feat(adapters): add Blaxel sandboxes and document Runloop, Beam and K…
reachjalil Oct 29, 2026
a7d93f8
feat(adapters): add Boat, E2B and Morph Cloud adapters, document Code…
reachjalil Oct 29, 2026
54d7f0a
feat(adapters): run sandboxes on AWS, Azure, Google Cloud and Northfl…
reachjalil Oct 29, 2026
14d5235
feat(adapters): add agent-sandbox and CubeSandbox pools (#28)
reachjalil Oct 29, 2026
a5b71d8
docs: list all 26 providers in the catalog with their tested status (…
reachjalil Oct 29, 2026
418fe92
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
be7fe5a
feat(opensandbox): add the Fast Sandbox runtime with native idle paus…
reachjalil Oct 29, 2026
4b9b631
fix(scheduler): requeue a sandbox whose create outlives the provision…
reachjalil Oct 29, 2026
4915a0f
docs: bring the website, README and docs up to date with the provider…
reachjalil Oct 29, 2026
7a9b8d6
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
95429e5
docs(providers): fit the capability table in the docs column (#35)
reachjalil Oct 29, 2026
9b6c32c
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
9239edf
docs: plain project copy on the website, without the decision records…
reachjalil Oct 29, 2026
e4448e0
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
c4916a3
feat(opensandbox): run Fast Sandbox live, with https port URLs and fo…
reachjalil Oct 29, 2026
12506c2
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
1b62d81
test(e2e): check Fast Sandbox microVM pools and desktop streaming (#41)
reachjalil Oct 29, 2026
752af71
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
8cf888a
chore(release): prepare v0.1.0-alpha.2 (#43)
reachjalil Oct 29, 2026
eebb59c
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
d4f6d75
test(providers): run the Freestyle and Daytona adapters against live …
reachjalil Oct 29, 2026
bcc71e3
fix(site): show a full pool's sandbox waiting in the launch video (#46)
reachjalil Oct 29, 2026
9864bc7
docs: record the live Daytona and Freestyle runs on the site and READ…
reachjalil Oct 29, 2026
ee768c8
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
389c085
fix(chart): let config pools use stored provider keys (#49)
reachjalil Oct 29, 2026
ff2e262
fix(server): finish a lifecycle change when the caller stops waiting …
reachjalil Oct 29, 2026
3fa647a
feat(server): list only the pools a tenant may use in capabilities (#51)
reachjalil Oct 29, 2026
e98a924
fix(opensandbox): set the Fast Sandbox clock and wait out long checkp…
reachjalil Oct 29, 2026
88fb914
feat(console): add an admin console you can host anywhere (#52)
reachjalil Oct 29, 2026
6c52f7d
chore: merge main back into pre-release
github-actions[bot] Oct 29, 2026
d9d8da2
docs(deploy): record a verified Azure install serving OpenWork (#55)
reachjalil Oct 29, 2026
bce0398
feat(scheduler): paused sandboxes give their slot back, with queued r…
reachjalil Oct 29, 2026
c1fe6a0
docs: bring the docs, maintainer guide and website up to date with wh…
reachjalil Oct 30, 2026
b4b67c1
test(store): give the released-slots conformance test a pool row to l…
reachjalil Oct 30, 2026
6cbef22
feat(console): show queued resumes and released slots, with the SDK f…
reachjalil Oct 30, 2026
e41fbf3
perf: measure Fast Sandbox desktop pauses and shorten them with spars…
reachjalil Nov 2, 2026
b8dd698
fix(lifecycle): show pausing and keep work off a sandbox mid-pause (#61)
reachjalil Nov 2, 2026
2709cc2
docs(lifecycle): record how pauses behave on a real Fast Sandbox (#62)
reachjalil Nov 2, 2026
9ad84c4
ci: run the Python SDK job on an image that still has Python 3.9 (#81)
reachjalil Nov 10, 2026
449a98a
fix(scheduler): keep sandboxes queued when a placement policy cannot …
reachjalil Nov 10, 2026
a88f603
fix(reconciler): never delete or adopt backend instances Differently …
reachjalil Nov 10, 2026
db7cfbb
fix(server): reject a negative or zero ttlSeconds (#74)
reachjalil Nov 10, 2026
43b9cc8
fix(scheduler): keep placing sandboxes while a pool does not answer (…
reachjalil Nov 10, 2026
5e11d11
fix(scheduler): let a concurrent delete or TTL change win over a stal…
reachjalil Nov 10, 2026
ca024b7
fix(adapters): keep the backend's TTL backstop behind Differently's o…
reachjalil Nov 10, 2026
68ca0b4
fix(pools): keep a working adapter when its credential cannot be read…
reachjalil Nov 10, 2026
767fbdf
fix(cli): send the dev operator token to loopback addresses only (#79)
reachjalil Nov 10, 2026
a604997
fix(client): let a streamed exec run longer than the request timeout …
reachjalil Nov 10, 2026
d58aea1
docs(changelog): record the placement, scheduler and client fixes (#82)
reachjalil Nov 10, 2026
610e0bd
chore(build)!: keep only the verified adapters in the default build (…
reachjalil Nov 11, 2026
4a372b2
docs(sdk): install the SDKs from this repository, not from a registry…
reachjalil Nov 11, 2026
6688793
fix(audit): show the newest events in the CLI, dashboard and console …
reachjalil Nov 11, 2026
b16dc81
ci: vet the end-to-end and live test files (#86)
reachjalil Nov 11, 2026
2bdeb69
ci(release): run the checks before a tag publishes anything (#87)
reachjalil Nov 11, 2026
8190cea
chore(release): prepare v0.1.0-alpha.3 (#88)
reachjalil Nov 11, 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
24 changes: 20 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,26 @@ jobs:
go-version-file: go.mod
- name: gofmt
run: make fmt-check
- name: vet
run: go vet ./...
- name: vet (including the e2e and live test files, which are not run here)
run: make vet
- name: test
run: go test -race -count=1 -coverprofile=coverage.out ./...
- name: cross-build linux amd64/arm64
run: make build-linux

# The adapter kinds left out of the default build (experimental_adapters
# tag) must keep compiling and passing their fake-server tests.
go-experimental:
name: go (experimental adapters)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: build, vet and test with -tags experimental_adapters
run: make experimental

postgres:
name: postgres conformance
runs-on: ubuntu-latest
Expand Down Expand Up @@ -90,7 +103,10 @@ jobs:

sdk-python:
name: sdk (python ${{ matrix.python }})
runs-on: ubuntu-latest
# ubuntu-latest moved to Ubuntu 26.04, which has no Python 3.9 build for
# setup-python. The SDK still declares requires-python >= 3.9, so keep
# testing it on the last image that provides it.
runs-on: ubuntu-24.04
strategy:
matrix:
python: ["3.9", "3.13"]
Expand Down Expand Up @@ -177,7 +193,7 @@ jobs:
# again. It approves what CI checked, not what a reviewer read.
approve:
name: approve when CI passes
needs: [go, postgres, sdk-typescript, console, sdk-python, helm, images, dco, release-source]
needs: [go, go-experimental, postgres, sdk-typescript, console, sdk-python, helm, images, dco, release-source]
if: >-
always() &&
github.event_name == 'pull_request' &&
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,23 @@ permissions:
attestations: write

jobs:
# Nothing is published from a tag whose code does not pass the checks CI
# runs: a tag can be pushed on any commit, checked or not.
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: gofmt, vet and tests (default and experimental builds)
run: make fmt-check vet test experimental
- uses: azure/setup-helm@v4
- name: helm lint
run: make helm-lint

binaries:
needs: verify
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -66,6 +82,7 @@ jobs:
dist/differently.spdx.json

images:
needs: verify
runs-on: ubuntu-latest
strategy:
matrix:
Expand Down
35 changes: 27 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ for humans).
- Never push to or open feature PRs against `main`; releases reach `main` only
through a release PR (see CONTRIBUTING.md).
- When CI is green you may merge your own PR into `pre-release`
(`gh pr merge --squash`), or queue it with `--auto`. CI's `approve when CI
passes` job approves same-repository PRs once every job is green; that
approval is what `main`'s ruleset needs for release PRs.
(`gh pr merge --squash`). `pre-release` has no required checks, so
`--auto` cannot wait for CI there: check the run, then merge. CI's
`approve when CI passes` job approves same-repository PRs once every job
is green; that approval is what `main`'s ruleset needs for release and
sync PRs ([releasing](docs/maintaining.md#releasing)).
- One focused outcome per PR, Conventional Commit title, `git commit -s`.

## Before you push
Expand All @@ -22,29 +24,46 @@ for humans).
make verify
```

It runs gofmt, go vet, Go tests with `-race`, the SDK tests and the Helm lint,
including the replay of recorded OpenSandbox runtimes. For changes touching
`internal/store/postgres`, also run `make test-postgres`. For anything a real
It runs gofmt, go vet, Go tests with `-race`, the same build, vet and tests
with `-tags experimental_adapters` (`make experimental`), the SDK tests and
the Helm lint, including the replay of recorded OpenSandbox runtimes. For changes touching
`internal/store/postgres`, also run `make test-postgres`. For `console/`, run
`npm run typecheck && npm run build && npm run build:server` there; for
`site/` or any page it publishes from `docs/`, `npm run build` in `site/`
(it fails on a broken link). For anything a real
backend or cluster could break, pick the cheapest rung of the verification
ladder in [docs/maintaining.md](docs/maintaining.md#prove-a-change-at-the-right-cost)
that covers it, and say in the PR what did not run.

## Map

- Contracts: `pkg/api` (wire types and error codes), `pkg/adapter`
(backend contract), `api/openapi.yaml` (HTTP).
(backend contract), `api/openapi.yaml` (HTTP). A new field or route also
goes into both SDKs (`sdk/typescript`, `sdk/python`) and, where people
see it, the dashboard and the console.
- Placement and security decisions: `internal/policy`, `internal/auth`,
`internal/store` (atomic admission/activation). Treat changes here as
security-sensitive and keep the tests in `internal/server/server_test.go`
(acceptance scenarios A–L) green.
- Backends: `internal/adapters/*` (`opensandbox` is the default private
runtime; Differently runs no sandboxes itself), `internal/wiring` (adapter
factories), `internal/pools`. The real-stack suite is `test/e2e`
factories), `internal/pools`. The default build holds `opensandbox`,
`daytona`, `freestyle`, `remote`, `devhost` and `fake`. Every other
adapter, and the `cloudhttp`, `pyshell` and `shellcmd` helpers, carries
`//go:build experimental_adapters`: a plain `go build`, `go vet` or
`go test` does not see them, so use `make experimental` when you touch
them or anything they share (`httpx`, `pkg/adapter`). The real-stack suite is `test/e2e`
(`make e2e-prepare e2e-up e2e`, or against any install).
- Recorded runtimes: `internal/adapters/opensandbox/testdata/recordings`,
replayed by `TestReplayRecordings`; re-record with the live test after an
OpenSandbox upgrade or a new adapter call.
- Stored credentials: `internal/secrets` (sealed values, `secret:` refs).
- Capacity: `internal/scheduler/capacity.go` (slots, resume queue,
preemption, lifecycle limits) and its model `internal/capsim`
([capacity](docs/scheduling/capacity.md)).
- Interfaces: `internal/dashboard` (the built-in dashboard), `console/` (the
separate admin app, static or on Node with sign-in), `site/` (the website;
its docs sync from `docs/**`).
- How to change, prove, re-record, optimise and release:
[docs/maintaining.md](docs/maintaining.md).
- Docs that must stay truthful: `docs/feature-parity.md`, `docs/roadmap.md`,
Expand Down
187 changes: 186 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,190 @@ All notable changes are recorded here. The format follows

## [Unreleased] — on `pre-release`

## [0.1.0-alpha.3] — 2026-11-10

Third alpha, published as a GitHub pre-release. Not for production. It is
mostly a correctness release: a review of the code found ten defects in
placement, the scheduler, the reconciler and the clients, and each is fixed
with a test that fails on the old behaviour. The default build is cut to the
adapters that have run against real backends, and the release workflow now
runs the checks before it publishes.

What has not run for this release: the end-to-end suite and the live adapter
tests. They last ran on the code before these changes (see `Verified` under
0.1.0-alpha.2 and the sections below). The fixes are covered by in-process
tests and, for the store, by the Postgres conformance job in CI.

Upgrading from 0.1.0-alpha.2:

- A config file or API call that names one of the 13 experimental adapter
kinds is refused by the published image (first section below).
- A client that lists sandboxes by status needs to add `pausing`.
- `PATCH /v1/sandboxes/{id}` with `ttlSeconds: 0` answers 400; use `DELETE`.

### Documentation and CI

- The SDK READMEs install the SDKs from this repository. Neither is
published: `pip install differently` installs an unrelated project of that
name, and `npm install @differently/sdk` finds nothing. Their method
tables no longer list `admin.agents.list`, which does not exist.
- `make vet` and CI compile and vet the end-to-end and live test files
(`-tags "e2e live"`), which no job runs, so they cannot stop compiling
unnoticed.
- The release workflow runs gofmt, vet, the Go tests (default and
experimental builds) and the Helm lint before it publishes binaries, the
chart or images.
- The chart's `appVersion` names this release, so installing the chart from
a checkout pulls an image that exists. It used to name `0.1.0`, which was
never published; the air-gapped and day-2 guides named the same tag.

### Changed (breaking): the default build holds only the verified adapters

The published image, the release binaries and `make build` now contain the
`opensandbox`, `daytona` and `freestyle` adapters, the `remote` plugin
protocol and the dev-mode `devhost` and `fake`. The other 13 adapter kinds
are experimental and compile in only with the Go build tag
`experimental_adapters`: `agent-sandbox`, `cubesandbox`, `e2b`, `boat`,
`morph`, `vercel`, `fly`, `blaxel`, `aws-agentcore`, `azure-sandboxes`,
`azure-sessions`, `gcp-sandboxes` and `northflank`. Apart from
`agent-sandbox`, which ran live on a kind cluster, they have only been
tested against fake servers. No adapter code was removed, and CI still
builds, vets and tests it with the tag.

- **If you use one of the 13 kinds**, the next published image will not
start with a config file that names it: the error names the pool, the
kind and the build tag. A pool of such a kind added earlier through the
API stays registered and reports `unavailable` with the same reason.
Build the server with `go build -tags experimental_adapters`, or the image
with `--build-arg GO_TAGS=experimental_adapters`
([provider catalog](docs/providers/README.md#what-the-server-contains)).
- `POST /v1/admin/pools` answers 400 `invalid_request` for these kinds on a
default build.
- Helm: pools of these kinds are refused unless `server.experimentalAdapters`
is `true`, which you set together with a `server.image` built with the
tag.
- The dashboard, the console and `differently admin pool add --help` list
only the default kinds. Experimental pools are added in the config file,
with the CLI or through the API.
- `make verify` and CI gained `make experimental` (build, vet and test with
the tag). The live tests of `agent-sandbox` and `cubesandbox` now need
`-tags live,experimental_adapters`.

### Fixed (placement, scheduler and clients)

Found by reading the code, each with a test that fails on the old behaviour.
None has run against a live backend.

- Placement no longer treats a policy it could not read as a policy that
does not exist. During a database error, a sandbox whose organization sets
`external: never` or denies a pool could be placed with that scope left
out. The sandbox now stays queued, and create, the placement explanation
and `GET /v1/capabilities` answer 503 `provider_unavailable`.
- The reconciler never deletes or adopts a backend instance that carries no
sandbox ID. The `daytona` and `freestyle` adapters check ownership
themselves instead of trusting the provider's label or metadata filter,
and Daytona's create reuses a sandbox only when its label carries the ID.
A provider that ignored the filter would have had every sandbox in the
account deleted as an orphan. The other provider adapters still rely on
their backend's filter for the installation match.
- A negative `ttlSeconds` on create is rejected; it used to pass the
`maxTTLSeconds` quota and give a sandbox with no expiry. A `PATCH` must set
a positive `ttlSeconds`: 0 used to delete the sandbox within a second and
now answers 400, so a client that ended sandboxes that way needs `DELETE`.
- A pool that stops answering no longer slows placement on the others. Pool
probes and the reconciler ran on the scheduler loop and held it for their
timeouts (5 s per probe, 30 s per list); they now run beside it.
- A delete or a TTL extension that lands while the scheduler is deciding now
wins. Expiry, the reconciler's "failed" and crash adoption wrote the status
without reading the record again, so an extended sandbox could still be
expired and a deleted one could come back as running.
- OpenSandbox, Daytona and Freestyle are given the TTL plus five minutes as
their own deletion deadline. Theirs counts from the create request and
Differently's from the moment the sandbox is ready, so the backend deleted
first and the sandbox ended as failed instead of expired.
- A pool keeps its working adapter when its credential cannot be read for a
moment. One failed read used to leave the pool without an adapter until a
later refresh. A deleted secret or file still takes the pool down, and a
changed value still rebuilds the adapter.
- The CLI uses the dev server's operator token only for `localhost`,
`*.localhost` and loopback addresses. It used to send it to any URL
containing "localhost", and not to `127.0.0.1`.
- A streamed exec through the Go client (`differently sandbox exec`) is no
longer cut off after ten minutes.
- The audit views show the latest events. `differently usage audit`, the
dashboard and the console asked for the first page, which is the oldest
events, so anything after the first 100 or 200 never appeared.
`GET /v1/audit-events` takes `order=desc` (newest first) and `beforeSeq`;
the TypeScript and Python SDKs take `order` and `beforeSeq`/`before_seq`.
- CI: the Python SDK job runs on `ubuntu-24.04`, the last runner image with
a Python 3.9 build; on `ubuntu-latest` it failed at setup.

### Fixed (lifecycle)

- A sandbox being paused reports the new status `pausing` instead of `running`
and refuses exec, file and port calls. A Fast Sandbox VM runs on for minutes
after its memory dump, and what it did then was lost on resume while every
call reported success. A resume during a pause calls it off on Fast Sandbox,
keeping the sandbox as it is; on other backends it waits for the pause. A
client that lists sandboxes by status needs to add `pausing`.
- Idle preemption no longer pauses sandboxes in use. Without an idle timeout of
their own their activity was never recorded, so they all looked idle a minute
after starting, and a running command now counts as activity until it ends.

### Added (capacity)

- A paused sandbox on a pool that checkpoints on pause
(`lifecycle.pause-checkpoint`: OpenSandbox Fast Sandbox, Freestyle) gives
its slot back. Resuming takes one again; on a full pool the resume waits in
a queue (202) and runs when a slot frees.
- Pool preemption (`preemption.mode: idle | lru`) pauses running sandboxes to
make room for waiting work; `preemptible: false` opts a sandbox out.
- Lifecycle limits `maxRunSeconds` and `maxPausedSeconds` (request, `PATCH`
and operator defaults).
- Capacity metrics (`differently_pool_slots`, `differently_resume_wait_seconds`,
`differently_preemptions_total` and more) and a capacity model,
`cmd/differently-capsim`, with results in `docs/scheduling/capacity.md`.
- The TypeScript and Python SDKs take `maxRunSeconds`, `maxPausedSeconds`
and `preemptible` on create and update, and return `pausedAt`,
`slotReleased`, `resumeRequestedAt`, and a pool's `preemption` and
`released`. The console marks queued resumes, released slots and idle
sandboxes, and the Providers page counts paused sandboxes that hold no slot.
- `differently_pause_seconds{pool}`: how long each pause Differently asks for
takes on its backend, the time a checkpointing pool needs to free a slot.
- `differently-capsim -pause` and `-resume`; the Workbot model's pause is the
measured 182 s.

### Documentation

- `docs/research/fast-sandbox-pause-time.md`: where a Workbot desktop's
pause on Fast Sandbox spends its time (memory dump, a checksum pass that
reads the whole checkpoint back, a single-stream upload, all limited by
the state-root disk), what was measured before and after leaving templates
sparse (median 216 s → 180 s, resumes unchanged, state kept), ranked
candidates, and two Fast Sandbox patches (sparse pull, parallel multipart
upload). The pools guide gains "Shortening pauses"; `test/pause` holds the
harness that measured it.
- `docs/research/fast-sandbox-shared-image-cache.patch`: stops Fast Sandbox's
image cache GC deleting the checkpoints paused sandboxes resume from, which
turned a 2 s resume into two minutes.
- `docs/deploy/azure-vms.md`: a verified install on two Azure VMs with Fast
Sandbox microVMs, managed Postgres, ACR and Key Vault, serving OpenWork's
Workbot computers, and what it took (guest kernel, template registry,
Key Vault CSI on VMs, ACR token scopes, sandbox network policy).
- The OpenWork guide records the end-to-end run, Freestyle template slugs
and OpenSandbox's maximum timeout; the e2e suite runs on installs with
several pools and replicas, and gives Fast Sandbox checkpoints time.
- The README, roadmap, feature parity, end-to-end and keys pages, and the
website now state the same verified results: the end-to-end suite on a
Fast Sandbox pool, two-node k3s, and stored provider keys in use on Azure.
The website lists capacity and the admin console, and its sidebar gains
Capacity, Keys and providers, OpenWork and Maintaining.
- `AGENTS.md` and `docs/maintaining.md` record what building and releasing
taught so far: where capacity, the console and the website live and how to
check them, the lists a new provider kind touches, new traps (Fast Sandbox
clock and checkpoints, template builds, zero times, CI on conflicting PRs),
and how release tags and `main` syncs are done.

### Added (admin console)

- `console/`: an admin app built with Astro, separate from the server. It has
Expand Down Expand Up @@ -402,6 +586,7 @@ OpenSandbox server on its Docker runtime.
beyond unit and in-process API tests: no live provider account, real
cluster or multi-replica run yet.

[Unreleased]: https://github.com/different-ai/differently/compare/v0.1.0-alpha.2...pre-release
[Unreleased]: https://github.com/different-ai/differently/compare/v0.1.0-alpha.3...pre-release
[0.1.0-alpha.3]: https://github.com/different-ai/differently/releases/tag/v0.1.0-alpha.3
[0.1.0-alpha.2]: https://github.com/different-ai/differently/releases/tag/v0.1.0-alpha.2
[0.1.0-alpha.1]: https://github.com/different-ai/differently/releases/tag/v0.1.0-alpha.1
Loading
Loading