Skip to main content

Chatticus

Computer snapshots

A Chatticus computer is a workplace identity (computer_id), not a particular Mac, Fargate task, or EC2 instance. Any machine that can run the computer image is a host. A garage Mac is a host in the same sense Fargate is a host. It is cheaper when it is on. It is not a different kind of computer.

Hosts do not share a running container. Live migration is out of scope. The durable workplace is a snapshot in S3. A host that should run the computer hydrates that snapshot onto local disk, runs, then publishes again before someone else takes over.

For how hosts are chosen from declared work kinds (manifold; not implemented), see Computer manifold.

What is stored where

The Ubuntu image (Xvfb, Chromium, worker, agent) lives in a registry (ECR). Every host already pulls that image. Do not copy OS layers from a Mac to AWS or back.

The snapshot is only the durable workplace:

Object In the snapshot Not in the snapshot
/workspace yes
Chromium profile (cookies, logins) yes
Image layers / apt packages registry image; treat extra packages as replaceable
Bot memory, chats, skills DynamoDB
Secrets Secrets Manager

Canonical layout:

s3://chatticus-{env}/tenants/{tenant_id}/computers/{computer_id}/
  snapshot.tar.gz       workspace + browser profile (v2: one gzip member per file)
  index/{checksum}.json every entry: path, kind, size, mode, sha256, link target, member offset and length
  manifest.json         image digest, checksum, index_checksum, published_by, published_at

Snapshot v2. The pack is still a valid .tar.gz, so hydrate unpacks it exactly as before, but each file is its own gzip member inside the tar stream. The index (conversation/src/snapshot/index-format.ts) records, for every entry, the byte range of its member, so the Front Door can list a computer's files and read one file with a single ranged GET while the computer is off. A publish writes the pack, then the index, then the manifest, which is written last and names the index (index_checksum, the SHA-256 of the index bytes). A manifest without index_checksum is a v1 snapshot: it still hydrates, and the file routes answer 409 snapshot_not_indexed until the computer publishes again or conversation/bin/reindex-snapshots.ts runs. The pack is streamed to S3 as a multipart upload, so the owner's memory does not grow with the workspace. Directories named node_modules, .next, dist, build, target, .venv, __pycache__ and .cache (at any depth), and any file over 50 MiB, are left out of the pack and the index; a skipped file is logged as snapshot_file_skipped. A publish scans size and modified time against the last index and hashes only files that moved, so an unchanged disk is not packed again.

manifest.json names the image digest the snapshot was taken against. A host whose local image does not match still hydrates the workplace files; it does not docker-load a private OS.

Why not "just leave it in S3 and mount it"

S3 is the object every host can see. It is a bad live disk for Chromium and overlayfs: high latency, no POSIX semantics that a browser profile needs, and concurrent writers from two hosts would split the brain.

Each running host has a local cache (Docker volume, bind-mount, or EBS). S3 is the last published checkpoint. Using a computer without copying bytes onto that host is not a goal. Seeing the same checkpoint without administrator-to-administrator copies is.

EFS can back /workspace on AWS hosts that share a VPC. It does not attach to a home Mac. The Mac still hydrates from S3. Do not design two durable stores. S3 is canonical. EFS, if used, is an AWS-side cache of the same files.

Publish, hydrate, relocate

No magic mover. Three administrator-or-worker operations:

  1. Publish. The host that currently has the live disk stops writes, packs /workspace and the browser profile, uploads to the computer's snapshot URI, and records the checksum. The computer is no longer dirty.
  2. Relocate. An administrator names the next host. The control plane does not copy a container. It records intended_host_worker_id and hydrate_required. Turns for that computer go only to that host until it hydrates. Other hosts of the same computer_id do not run it in the meantime (no split brain).
  3. Hydrate. That host downloads the snapshot (or hits a local cache with the same checksum), unpacks onto its volume, and clears hydrate_required. Prefer-local ranking resumes.

Copying "a bunch of containers from AWS to my Mac" is this sequence aimed at the Mac: publish on AWS if needed, relocate to the Mac worker, the Mac hydrates. The other direction is the same with Fargate or EC2 as the target.

Failover when a prefer-local Mac's heartbeat dies is the same hydrate path on an AWS host, without an administrator picking the target. That is not live migration. It is "start from the last snapshot." Work that was never published is gone, same as an unsynced volume.

Dirty disk

Writes on a host mark the computer dirty. Relocate is rejected until the current host publishes. That is the administrator safety: do not point the Mac at S3 and throw away the Fargate volume's last hour of cookies.

An explicit discard (not v1 kernel) is the only way to relocate without publishing.

What we will not build

The control-plane kernel records snapshot URI, checksum, dirty, intended host, and hydrate-required. Hosts pack /workspace and the browser profile into snapshot.tar.gz plus manifest.json.

A filesystem object store is the local stand-in. Production uses S3:

Organization Bucket Who creates it
Anthus-managed CDK stack ChatticusSnapshots Anthus deploy
Customer-owned chatticus-snapshots-{OrganizationId} in the customer account Customer runs infra/customer-role.yml

Hosts set CHATTICUS_SNAPSHOT_BUCKET to the bucket name for their organization. Anthus-managed hosts read the CDK SnapshotBucketName output. Customer Fargate hosts read the value wired by the ChatticusComputers stack parameter of the same name. Do not create either bucket with the AWS CLI.

Object keys keep the canonical prefix tenants/{tenant_id}/computers/{computer_id}/ inside whichever bucket holds the organization.

If CHATTICUS_SNAPSHOT_BUCKET names a bucket that does not exist yet, hydrate and publish skip without crashing the host. The customer must re-run or update the cross-account template to create the bucket.

cd infra
npm install
npx cdk bootstrap
npx cdk deploy ChatticusSnapshots
export CHATTICUS_SNAPSHOT_BUCKET=<SnapshotBucketName>

node conversation/bin/snapshot.ts pack \
  --live-root ./var/hosts/fargate \
  --store s3 \
  --tenant anthus \
  --computer household-computer \
  --worker fargate-1

node conversation/bin/snapshot.ts hydrate \
  --live-root ./var/hosts/garage-mac \
  --store s3 \
  --tenant anthus \
  --computer household-computer

If the Mac already has a cache whose checksum matches the manifest, hydrate does not download the pack again.

Inside the computer image, /workspace is a symlink to /var/lib/chatticus/computer/workspace. Do not give two hosts the same live volume. They share the snapshot store only.

Prove relocate with two running containers:

sh computer/test_relocate.sh