~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
238 lines · 12.9 KB Raw History

The bidder: install, identity and upgrades

Short copy-paste commands are in CONTRIBUTING.md. This page records the installed shape and the facts behind those commands.

Release artifacts

Tagged releases publish Linux binaries and the guest image to a public bucket. No credentials.

PathHolds
releases/<version>/binaries/socialweb-computer-linux-amd64Host binary, Linux amd64.
releases/<version>/binaries/socialweb-computer-linux-arm64Host binary, Linux arm64.
releases/<version>/binaries/SHA256SUMSsha256 of both binaries.
releases/<version>/binaries/release.jsonCommit, build mode, one entry per platform.
releases/<version>/binaries/version-linux-<arch>.txtPinned version and sha256 of every embedded asset.
releases/<version>/manifest.jsonEvery file under the version, with size and sha256.
guest-images/firecracker-x86_64/<sha256>/vmlinux, rootfs.ext4, image.json; <sha256> is the pinned sha256 of image.json.

<version> is a git tag such as v0.0.0-rc3. main/<commit>/ holds commit builds and main/latest/ is the newest. A guest image is uploaded once, under the key of its pins, and every release with the same pins uses it.

Install

HANDLE=<your atproto handle>
BUCKET=https://socialweb-computer-kcp.sfo3.digitaloceanspaces.com
V=$(curl -fsSL https://api.github.com/repos/publicdomainrelay/socialweb-computer-kcp/tags \
  | jq -r '.[].name' | grep '^v' | sort -V | tail -1)
ARCH=linux-$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/')
mkdir -p /var/tmp/swc-release && cd /var/tmp/swc-release
curl -fLO "$BUCKET/releases/$V/binaries/socialweb-computer-$ARCH"
curl -fLO "$BUCKET/releases/$V/binaries/SHA256SUMS"
grep " socialweb-computer-$ARCH$" SHA256SUMS | sha256sum -c -
sudo install -m 0755 "socialweb-computer-$ARCH" /usr/local/bin/socialweb-computer
sudo /usr/local/bin/socialweb-computer install bidder --handle "$HANDLE"

install bidder creates the swc-bidder account, its subuid/subgid range and kvm membership, the state dir, the binary slot, config, guest image, systemd user units, linger and the kcp bootstrap. It ends by running check-host -role bidder as the account. State: /var/lib/socialweb-computer/swc-bidder; kcp at handles/<hash>/kcp (root only); unit socialweb-computer-bidder@<handle>.service in the account's user manager.

A re-run is idempotent: completed steps are skipped, and only a changed config or unit restarts the core. A re-run with another --handle adds an instance. --dry-run prints the step list. A dev binary needs --allow-dev-binary. --ready-timeout (default 1m) bounds the first /readyz wait. --vm-slots (default 256) reserves N * 65536 subordinate ids.

Guest image

--guest-image-source takes a file:// or https:// base, fetches image.json, vmlinux and rootfs.ext4, and verifies each against the sha256 pinned in the binary. A mismatch is refused.

# variables carry over: BUCKET, V
HANDLE=<handle>
PIN=$(curl -fsSL "https://raw.githubusercontent.com/publicdomainrelay/socialweb-computer-kcp/$V/internal/versions/versions.json" \
  | jq -r '.guestImages.targets["firecracker-x86_64"].outputs["image.json"].sha256')
sudo /usr/local/bin/socialweb-computer install bidder --handle "$HANDLE" \
  --guest-image-source "$BUCKET/guest-images/firecracker-x86_64/$PIN"

Without a source, the binary resolves the image pinned into its own build from guest-images/<target>/<its pinned image.json sha256>, then releases/<its version>, then main/<its commit>, then main/latest, then the newest published release. Every candidate is verified; only when all differ is the image refused. A stored image is re-verified.

Only firecracker-x86_64 is published. An arm64 host builds one with scripts/build-guest-image.sh.

Identity and association

The default identity is localRepo: the bidder's own PDS, never the operator's repo. install bidder creates the PDS account and writes the DID into MarketIdentity/bidder, the session into the bidder's OpenBao and the repo key there too. -- --atproto-oauth-qr signs in an existing account instead; that account's PDS holds the repo and the bidder runs no local PDS.

Association is one com.publicdomainrelay.temp.badgeBlueKeys record in the operator's repo with challenge the operator DID, keyId the bidder DID and service bidder_associate. scripts/associate-bidder.ts writes the record from a mode-0600 credentials file and prints neither handle nor password. The application's AtprotoRecordWatch/association verifies it and the identity controller writes the operator DID into MarketIdentity.status.associations; no manual patch is needed. spec.approvedAssociations is the declarative workaround, not the accepted path.

HANDLE=bobvmbuilder.bsky.social
ACCOUNT=BOB
CREDS=$HOME/.bobvmbuilder_bsky_social_source
STATE=/var/lib/socialweb-computer/swc-bidder
K=$(sudo sh -c "ls $STATE/handles/*/kcp/admin.kubeconfig" | head -1)
BIDDER_DID=$(sudo kubectl --kubeconfig "$K" get marketidentity bidder -o jsonpath='{.status.did}')
deno run -A --no-lock scripts/associate-bidder.ts --creds "$CREDS" --account "$ACCOUNT" --bidder-did "$BIDDER_DID"

A requester association uses the same helper with --requester-did; the helper derives service requester_associate.

Non-association mode: patch the root object and let the controller reconcile it. It sets Associated=True with reason AssociationSkipped and writes no record.

STATE=/var/lib/socialweb-computer/swc-bidder
K=$(sudo sh -c "ls $STATE/handles/*/kcp/admin.kubeconfig" | head -1)
sudo kubectl --kubeconfig "$K" patch marketbidder app --type=merge \
  -p '{"spec":{"identity":{"association":"Skip"}}}'

The bidder's own AtprotoRecord/association is applied through the app's PDS by the AtprotoRecord controller. The helper writes the operator's claim once; the verified claim is the proof, and the identity reports Associated=True.

Operator sign-in: the operator's vouches from the bidder kcp

The option operator-signin (plan 0039 D5) signs the operator's own account in next to the bidder, so ../examples/infra/vouch-bidder-operator.yaml applies unchanged in the bidder kcp, as in a market network. Set it once:

wherehow
install bidder--bidder-operator-signin <operator handle or DID>, written to config.json bidder-operator-signin
run bidder--bidder-operator-signin <handle>, or -- --operator-signin <handle> (wins)
config.json"bidder-operator-signin": "<handle>", or "bidder-options": "--operator-signin <handle>"

It composes, in the bidder's workspace, the names a market network uses:

  • MarketIdentity operator: mode oauthQr, spec.sessionScope atproto repo:sh.tangled.graph.vouch?action=create&action=update (the scope the identity checks and asks for; nothing else), the session at <mount>/identity/operator/oauth-session of the bidder's OpenBao, association Skip; the transferred QR session must be the named account
  • once it is Ready: RemotePDS operator-<hash> (its PDS), RemoteRepo operator, AtprotoRepo operator and the ComponentLink link-records-operator, which lets AtprotoRecords labelled apps.socialweb.computer/record-repo: operator write sh.tangled.graph.vouch through that repo

Without the option nothing of it is composed, and the bidder's own MarketIdentity bidder and its scope never change. The operator account should be the bidder's associated operator (MarketIdentity bidder status.approvedAssociations): bidder-tangled-vouch reads that operator's vouches.

STATE=/var/lib/socialweb-computer/swc-bidder
K=$(sudo sh -c "ls $STATE/handles/*/kcp/admin.kubeconfig" | head -1)
kb() { sudo kubectl --kubeconfig "$K" "$@"; }
URL=$(kb get marketidentity operator -o jsonpath='{.status.pendingAction.url}')
deno run -A --no-lock scripts/oauth-signin.ts --creds ~/.bobvmbuilder_bsky_social_source --account BOB --url "$URL"
kb get marketidentity operator -o jsonpath='{.status.phase} {.status.did}{"\n"}'
REQ=did:plc:...
sed -e "s/REPLACE-REQUESTER-DID/$REQ/" -e "s/2026-11-01T00:00:00Z/$(date -u +%Y-%m-%dT%H:%M:%SZ)/" \
  examples/infra/vouch-bidder-operator.yaml | kb apply -f -
kb get atprotorecords.socialweb.computer operator-vouch-requester -o jsonpath='{.status.phase} {.status.uri}{"\n"}'
  • Applied with at://<operator did>/sh.tangled.graph.vouch/<requester did>; the record is deletionPolicy: Retain, so a kubectl delete keeps it in the repo
  • the QR page decides the scope it asks the PDS for; a session granted without the vouch scope fails the identity's scope check ("lacks scopes the identity now needs"); sign in again once the QR page asks for it
  • the QR link expires after 5 minutes; read it right before the sign-in, never print it
  • -- --operator-signin-mode pdsAccount --operator-pds-url <url> signs the operator in with a password instead (the market network's mode, an app password on a hosted PDS): put it in the bidder's OpenBao at <mount>/identity/operator/keys, key password, before the bidder starts
  • removing the option stops composing them; the objects it made stay until you delete them (marketidentity, atprotorepo, remoterepo named operator); the records stay in the repo

Upgrade and uninstall

cd "$(git rev-parse --show-toplevel)"
C=$(git rev-parse HEAD)
BIN=$HOME/.tmp/socialweb-computer-build/release/$C/socialweb-computer-linux-amd64
SHA=$(sha256sum "$BIN" | cut -d' ' -f1)
STATE=/var/lib/socialweb-computer/swc-bidder
sudo install -d -m 0755 /var/tmp/swc-release
sudo install -m 0755 "$BIN" /var/tmp/swc-release/socialweb-computer-${C:0:8}
STAGED=/var/tmp/swc-release/socialweb-computer-${C:0:8}
sudo runuser -u swc-bidder -- env XDG_RUNTIME_DIR=/run/user/$(id -u swc-bidder) \
  CONFIG_PATH_SOCIALWEB_COMPUTER=$STATE/config.json \
  "$STATE/bin/current/socialweb-computer" upgrade --binary "$STAGED" --sha256 "$SHA"
sudo "$STAGED" install bidder --handle <handle>

The upgrade switches bin/current, keeps bin/previous, restarts the core and adopts every installed and transient unit. VMs survive. State, OpenBao, DID and association survive. rollback swaps the two binary slots and restarts.

sudo /var/lib/socialweb-computer/swc-bidder/bin/current/socialweb-computer uninstall bidder --purge

uninstall bidder --purge stops the units, removes the state dir (PDS repo, OpenBao, keys, VMs) and deletes the account install created. --force proceeds with active VM units; each stopped VM becomes Lost.

Terminate a VM from the bidder

A bidder operator ends a VM by deleting its ComputeVM in the bidder's kcp. Use it when the requester cannot release the contract (the tab or app that holds it is gone) or when the owner asks for a VM to be freed. It is the same path a requester's vm.delete takes, so nothing is left behind.

K=$(sudo -n -u swc-bidder sh -c 'ls /var/lib/socialweb-computer/swc-bidder/handles/*/kcp/admin.kubeconfig | head -1')
kb() { sudo -n -u swc-bidder env HOME=/var/lib/socialweb-computer/swc-bidder KUBECONFIG="$K" kubectl "$@"; }
kb get computevms.socialweb.computer -o custom-columns=NAME:.metadata.name,PHASE:.status.phase,CREATED:.metadata.creationTimestamp
kb delete computevms.socialweb.computer <name> --wait=false
kb get computevms.socialweb.computer

The name is fc-<first 12 chars of the requester DID after did:plc:>-<hash>; pick the VM by that DID prefix and its creation time, and never delete one you have not matched to the contract you mean to end.

What the delete does, in order (its finalizer, logged by the bidder):

  1. a termination StateSync, and a vm.terminated record pinned in the bidder's repo with reason deleted_on_request;
  2. the VM process, its unit and its work dir are removed and the contract is released;
  3. the finalizer is dropped and the ComputeVM is gone.

The vm.terminated event is also delivered to the requester; a 404 there only means the requester is gone, and the record in the bidder repo still stands. Every atirc token leased to the contract dies by its lease as soon as that termination record exists; a requester that is still running also revokes it. Check the host afterwards: kb get computevms.socialweb.computer is empty for that contract and no firecracker process remains for it.

Unit and logs

swc-bidder is a system account, so its user units log to the system journal.

sudo runuser -u swc-bidder -- env XDG_RUNTIME_DIR=/run/user/$(id -u swc-bidder) \
  systemctl --user status 'socialweb-computer-bidder@<handle>.service'
sudo journalctl _SYSTEMD_USER_UNIT='socialweb-computer-bidder@<handle>.service' --since -10min

journalctl --user as that account finds nothing; read the system journal as root with the _SYSTEMD_USER_UNIT filter.

See also