Repository navigation
feat: Krater Compute, the application form and project teams - #9
Merged
Merged
Conversation
This was referenced Oct 17, 2026
Merged
The client covers what Krater Compute needs: search offers, create, show, list, change bid, stop/start, destroy, logs and charges. It follows the facts from the 2026-10-11 spike: every path has its trailing slash and a redirect is an error (Vast's 308 does nothing), instances are listed through the paged v1 API, create returns only the instance id (the response's instance_api_key is dropped), log downloads go out without the org key, and money is parsed as Decimal. Hourly rates are stored as integer micro-dollars because bids need sub-cent precision. FakeVast serves the same API through httpx.MockTransport and copies the behaviours that cost money to get wrong (308 on a missing slash, 410 on the v0 list, 401 without the key, the 512-offer search cap). Only the worker will build a client; the fake is never used in production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every policy number in the Krater Compute design is now a global default
in compute_settings (one row, seeded with the values approved on
2026-10-11: reliability floor 0.95, bid margin 10%, pause grace 5 min,
move after 30 min, box idle 30 min, destroy after 24 h, reconciliation
alert at 10%), and admins can override any of them per project.
effective_policy() resolves a project's numbers for the compute loops.
Admins edit both on /admin/compute and /admin/compute/projects/{id}.
Changes need a reason, are bounds-checked (move after can't undercut the
pause grace, for any project), and write an AuditEvent with old and new
values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vast gives client accounts no price history (its market metrics answer host accounts only, and its public feed needs a data license for bulk use), so the worker samples it: one search per tracked GPU model and rental type, 1-GPU offers at or above the global reliability floor, and one gpu_price_samples row with the offer count and the min, p10 and median full hourly cost (GPU price plus 32 GB of the host's disk) in integer micro-dollars. A search that hits Vast's 512-offer cap is flagged. One failing model is skipped; a rate limit ends the run. Without a usable Vast client (no key, or the fake in production) the task does nothing. CLAUDE.md now records the micro-dollar rate unit and that only the worker talks to Vast. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A worker that started before the key file was readable, or whose key was rotated, kept using no client or the old key until a restart. Live mode without a readable key now logs an error on every call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compute_jobs, compute_instances and compute_events, and a scheduler pass the worker runs every minute under a procrastinate lock, only with the new KRATER_COMPUTE_MODE=vast (which production allows only with the live Vast API). Each pass: - settles creates whose response was lost: the instance row and its unique label are committed before Vast is called, so the machine is found by label, or given up after 5 minutes; - sweeps instances with Krater's label prefix that Krater has no live row for, and destroys them; - watches live instances: running, paused when outbid, the bid raised in steps after the pause grace up to the job's max, moved after "move after", max runtime, never started, vanished, and finished jobs; - starts queued jobs at the cheapest offer at or under the target (bid = min_bid + margin, clamped to the max), within the project's hourly cap, holding budget for the max runtime under the project's row lock. After max wait the job keeps waiting, raises to its max, goes on demand, or cancels, as the member chose. Every destroy is confirmed by show_instance and retried until it is. Members submit and cancel jobs through krater.services.compute_jobs; the runner, spend loop, reconciliation, boxes and UI come next. Until the runner exists the container runs the command in a shell that never exits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…label prefix Withdrawing a project (or approving its completion) only cancelled its queued jobs, so running machines kept billing until their max runtime, after the budget was reclaimed. The scheduler now cancels and destroys them on its next pass. Production with KRATER_COMPUTE_MODE=vast refuses the default label prefix: the sweep destroys unknown instances with its prefix, so two deployments sharing a Vast account must not share one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every scheduler pass, before it changes any state, each billing instance accrues its rented price since the last pass: disk from create (the image pull bills disk only), the GPU only while running, as Vast bills it. Job spend is the sum of its instances; project spend is written as a new vast_estimate SpendSnapshot whenever it grows, which feeds the project page, the budget alerts and Quilt's spend.recorded events unchanged. The estimate only grows; bandwidth is left to the hourly reconciliation. Then each project with active jobs is warned at the warn percentage and, projected one minute ahead, stopped at its ceiling: every active job fails with "budget reached" and its machines are destroyed. The warning and teardown audit events (and so the Slack alerts) now live in krater.services.budget_alerts, shared with the SkyPilot reconciler, whose behaviour is unchanged. The project page shows the budget banner in vast mode too. spend_snapshots.source is a non-native enum whose CHECK constraint autogenerate doesn't compare, so migration b81f0c6d2e57 replaces it by hand; a test inserts a vast_estimate snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An hourly task (under the scheduler's lock) reads /api/v0/charges for the days recent instances ran, sums each instance's charges (rows span days and are rounded to $0.001, so only cumulative totals are compared), and replaces the instance's estimate with Vast's figure. Each check is kept per project in spend_reconciliations; a difference past the project's alert threshold (and over a cent) writes an AuditEvent and shows on /admin/compute. Charges for Krater-labelled instances Krater never rented are flagged too. A corrected total above the latest snapshot becomes a new vast_estimate snapshot and a Quilt spend.recorded event. A lower one stays in Krater's own records: Quilt takes increases only until patchworklabsorg/quilt#17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vast passes `args` to the image's ENTRYPOINT, so an image with its own ENTRYPOINT would have swallowed `sh -c ...`, exited, and been restarted by Vast in a billing loop. In args mode Vast's `onstart` field is the entrypoint override (its CLI sends --entrypoint as onstart), so Krater now sends runtype args, onstart sh and args [-c, script]. Recorded as needing one paid check with an image that has an ENTRYPOINT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Charges for a running machine may lag what it has run, and the snapshot only rises, so lowering a live estimate to Vast's figure would make the budget stop fire late by that lag. A live instance now takes Vast's figure only when it is higher; a destroyed one takes it either way. Unknown charges are flagged once per source instead of every hour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Krater Compute's runner gets only presigned URLs for its own job's keys, never storage credentials. ObjectStore gains presign_put, list_prefix and multipart uploads (create, presign a part, complete), so a checkpoint larger than one part goes up in parts under Cloudflare's request-body cap in front of the storage host. Checkpoints and outputs get their own bucket (KRATER_COMPUTE_S3_BUCKET, default krater-compute), which the deploy-time ensure_bucket now creates too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each rented instance gets its own runner token (Krater stores only its sha256), sent to the machine as KRATER_JOB_TOKEN with KRATER_RUNNER_URL. It works only while that instance is live and not being destroyed, so a moved job's old machine is locked out. /runner/v1/ (Bearer token only, no session or CSRF, its own rate-limit group) lets the runner: - start: learn its command and whether this is a resume (a later attempt, or a restart of the container on the same machine); - heartbeat: report GPU use and sessions, and learn when to stop; - exit: report the exit code, once; the scheduler then destroys; - list, download and upload its own job's checkpoints and outputs through presigned URLs, multipart above 64 MB. The container now runs a bootstrap that fetches the runner from the portal with curl, wget or Python, checks its sha256, runs it, and then waits forever. The scheduler fails and destroys a running machine whose runner hasn't started within the runner start deadline (10 min, also after a resume) or stops sending heartbeats (30 min); both are new compute settings. Without a runner binary to serve, nothing is rented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runner/ is a standard-library-only Go program, built by a new Dockerfile stage as a static linux/amd64 binary that the portal serves. On the machine it reports start, restores the job's checkpoints, runs the command with sh -c (KRATER_RESUMED, KRATER_CHECKPOINT_DIR and KRATER_OUTPUT_DIR set), sends heartbeats with nvidia-smi's GPU use, stops the command when Krater says so, uploads changed checkpoints on a timer (skipping .tmp/.partial names) and checkpoints and outputs at the end through presigned URLs, multipart above 64 MB, and reports the exit code. A refused token makes it give up at once. Go tests run against a fake Krater-and-storage server, with -race. The bootstrap was also run end to end in alpine, python:3.12-slim and debian:bookworm-slim against a fake runner API: the first two ran the command and reported its exit code, the third explained it has no curl, wget or Python, and a tampered checksum was refused. docs/compute/runner.md documents the contract for members. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runner usually reports /start within seconds of the container starting, before the scheduler's next pass even notices Vast's running, so comparing runner_started_at with the time the pass saw running failed almost every real job at the 10-minute deadline. Each instance now has runner_starts_needed: 1 at first, and the runner's start count plus one whenever a pause is seen (Vast restarts the container on resume). Tests cover a start before the pass sees running, a restart reported before the resume is seen, and a stale start from before a pause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A runner holds its token and could otherwise ask for unlimited upload URLs on storage that alastor's other services share. - A per-project storage quota (a compute setting, 50 GB by default, migration d5e1a7b3c204): Krater sums the project's objects and refuses URLs past it; replacing a file counts only the difference. - Every presigned PUT and part URL is signed for its exact size. Checked against a throwaway SeaweedFS 4.47: the signed size gets 200, a bigger or smaller body 403. - An hourly task aborts multipart uploads whose job has ended (an hour's grace) or that belong to no job. SeaweedFS lists uploads without a start time, so job state decides, not age. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`python -m krater.compute.submit` queues, lists and cancels jobs as the project's submitter, through the same service and checks the member UI will use; run it where the worker runs. scripts/dev/compute_watchdog.py is the independent backstop for a paid test: it destroys every instance with one label prefix (and verifies each by id) once Vast's charges for them pass a cap or a deadline passes, and refuses a broad prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Interactive boxes need a stopped state (the GPU is freed, the disk kept) for both jobs and instances; a stopped box's job still counts as active, so its disk keeps billing against the budget and holds. Migration 4cd4032b89cb adds it to the hand-maintained enum CHECK constraints, the stop/resume and SSH columns on compute_instances, and two settings: the box sync deadline and the resume timeout (10 minutes each). Members add SSH public keys at /account/ssh-keys. Only ssh-ed25519, ecdsa-sha2-nistp* and ssh-rsa keys are accepted, only if the base64 decodes to a blob of the same type, and only `type base64` is stored, so nothing can add options or a second line to a box's authorized_keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A box is a job with no command that keeps a workspace open: - It launches in Vast's SSH mode (ssh_direct), its runner started in the background by Vast's startup script, all in launch.create_request and marked pending the paid check. Krater records where to SSH (the direct 22/tcp port when mapped) and hands the runner its owner's SSH keys. - Krater refuses to start boxes while the Vast account has any SSH keys, since those may reach every machine. - Idle (runner reports GPU under 5%, CPU under 10%, no session) for the box idle time, or stopped by the member: the next heartbeat asks the runner to save /workspace, and Krater stops the machine once it confirms or the sync deadline passes. A stopped box bills disk only, stays an active job for budgets, and is destroyed after the destroy time, with a warning an hour before. - Resume restarts the same machine; the runner must start again. If the machine's GPU is taken, Krater takes the start back and tells the member when the workspace was last saved; moving it elsewhere is their choice, never automatic. A box started outside Krater is billed and stopped. - A box whose runner goes silent after it has run is stopped, not destroyed, so its disk survives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a box the runner writes its owner's keys as the whole of ~/.ssh/authorized_keys (dropping anything that isn't one `type base64` line), restores /workspace, reports open SSH sessions counted from /proc/net/tcp and tcp6 with each heartbeat, saves /workspace on a timer and whenever Krater asks before stopping the box, then confirms. A file the storage quota refuses no longer stops the rest of an upload pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With KRATER_COMPUTE_MODE=vast, a project's team runs jobs and boxes from
/projects/{id}/compute: a list with each job's state and why, a submit
form (GPU models or minimum memory, disk, target and max price,
interruptible, max wait and what happens after, max runtime) next to the
latest sampled prices, and a job page with its machine, the SSH command
for a box (Jupyter through an SSH tunnel), stop, resume, move and
cancel, and its event log. Reviewers and admins can look; only the team
can launch. The project page links there.
/pricing/history draws Krater's own price samples per GPU and rental
type: the cheapest offer as a line and the 10th-50th percentile band in
the same hue (a three-colour version failed the colour-vision check in
dark mode), server-rendered SVG with a legend, a hover crosshair and
tooltip from a small script, and the same numbers as a table. Checked by
eye in light and dark mode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Max runtime counted wall time since the first start, so a box stopped overnight was destroyed on resume. Instances now add up time actually running (run_seconds, migration e7a2c9d1f305; reset when a box is resumed), and a box at its max runtime is stopped, never destroyed. Jobs no longer lose runtime to pauses either. - Vast keeps reporting running for a while after a stop. A stopped box seen running now counts as a member restart only if Vast's intended state is running too, so Krater's own stop isn't billed as an hour of GPU. FakeVast can lag the same way. - An error from the account SSH-key check no longer stops the whole pass: boxes are refused for that pass, jobs still start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing is removed yet; this only moves and renames, so the deletion that follows is a pure deletion. - The public GPU price catalog (SkyPilot's Vast CSV, fetched from GitHub) that /pricing and the budget estimator read now lives in krater/pricing/catalog.py with its own fake, chosen by pricing_mode. - LAUNCHABLE_STATUSES and hourly_cap_cents move to krater/services/launch_rules.py. - Renamed settings: pricing_mode, pricing_catalog_url and its timeout, compute_max_hourly_cost_cents, budget_warn_percent. Each still reads its old KRATER_SKYPILOT_* env name for one release (pydantic AliasChoices; the new name wins), so Krater can deploy before its env files change. Production now refuses a fake pricing catalog by pricing_mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Krater Compute replaces it (docs/compute/). Deleted: - krater/skypilot, krater/skypilot_policy, services/skypilot_sync.py, services/launch_policy.py, the /internal/skypilot/policy route and its rate-limit group, the skypilot_reconcile task and its CLI; - the skypilot and auth-proxy compose services, their volume and the Caddy route for the SkyPilot hostname; - the nightly SkyPilot contract workflow and its scripts, the SkyPilot docs and the tests for all of the above; pyyaml, which only the launch gate used. KRATER_COMPUTE_MODE is now off or vast, and off by default so a developer with a live Vast key can't rent by accident. The project page shows the budget banner from krater.services.budget_alerts and links to Compute; the admin cap form no longer mentions SkyPilot. The worker (only) can mount a Vast key file in compose. The public price catalog behind /pricing stays (krater/pricing/catalog.py), as does the skypilot_cost_report spend source, which existing rows use. The projects.skypilot_* columns are dropped in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing reads them since SkyPilot was removed (migration f3b8d4e60a12; the downgrade restores them empty). SpendSource.skypilot_cost_report and the old audit action names stay, since existing rows use them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SPEC, CLAUDE.md, the production and staging runbooks, the pricing doc and the member guide to running when compute is cheap now describe Krater Compute; small references in the Slack, storage, Quilt and Weave docs are fixed; HANDOFF and FUTURE are marked historical where they describe SkyPilot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…duction-only checks /pricing linked to /pricing/history even with compute off, where the worker has no Vast key and the history stays empty. The link now shows with KRATER_COMPUTE_MODE=vast or once samples exist. docs/compute/README.md lists what the staging round can't check because production sits behind Cloudflare: that a datacenter IP can fetch the runner without a challenge, and that size-signed presigned uploads survive the proxy. Checked locally: the real procrastinate worker against FakeVast ran the scheduler, price history, reconciliation and the rest under their locks with no errors, rented the queued job and held its budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…price-history searches
Staging, 2026-10-11: Vast twice answered a create with {"success": false} and no message, and rented the
machine anyway. Krater requeued the job and the sweep destroyed the machine a minute later, in a loop. Such an
answer is now unconfirmed, like a lost one: the next pass finds the machine by its label. A refusal without a
message names the answer's fields (never their values).
The price-history run hit Vast's rate limit after 33 searches in 4 s; searches are now 1 s apart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`krater ssh box -- -i key nvidia-smi` built `ssh -p N -i key nvidia-smi root@host`, so ssh took the command for the host name. Found running a box on a real Vast machine (staging, 2026-10-14). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On staging (2026-10-14) Krater went down with a box running, and the box billed for four hours with nobody watching: the runner only logged its failed heartbeats. Now a runner that hears nothing from Krater for the new runner_self_stop_minutes policy (3 hours by default, admin-adjustable and per project) stops its own machine through Vast's API with the container's own key. It stops rather than destroys, so the disk keeps what the job or member did meanwhile. Back up, Krater tells a self-stop from an outbid machine by the runner's silence, starts a job's machine again (it resumes from the checkpoints on its disk) and shows a box as stopped. Also: the scheduler's first pass after Krater itself was down failed every running job for silent heartbeats before any could get through. A live instance not looked at for 5 minutes now restarts its runner deadlines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A limit order is one price (target = max) that holds when outbid: Krater bids the offer's minimum plus the margin, raises up to the limit, and once past "move after" keeps the paused machine and its disk unless another machine at or under the limit is free. Krater holds the order itself, so it works the same with any provider. - compute_jobs.on_outbid (move | hold); the API takes limit_usd_per_hour and on_outbid; the new-job form defaults to a limit order. - krater submit --limit, --expires, --on-outbid; prices shown as "limit". - Fix _raise_bid re-raising to the same bid every pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Krater Compute now reaches its provider through `krater.providers`: capabilities it branches on (never a provider's name), the protocol the scheduler, reconciler and price history call, neutral errors that Vast's subclass, and a registry keyed by KRATER_COMPUTE_MODE. Vast is the only provider and behaves as before. - compute_instances.provider; a pass watches, settles and reconciles only its provider's rows; instance ids are unique per provider. - Job validation refuses what the active provider can't do (boxes, interruptible, on demand, limit orders, raise_bid, on_demand). - The runner's switch can be turned off (-1) for a provider whose machines can't stop themselves; reconcile runs only with charges. - The runner no longer says "Krater stops this machine" after stopping itself; self-stop reasons read "30 minutes" rather than "0.5 hours". - docs/compute/providers.md: adding a provider, what is still Vast-shaped, AWS/GCP mapping, and the OSPool report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is stopping - A <job> can be its name: the active job of that name wins over ended ones, and several ended ones are listed. - krater download JOB DIR (--to still works). - krater stop says the box is stopping, not "running". - krater show gives the ssh command only while the box runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A site setting on /admin (audited, reason required): full name, first
name only (the default), or not named ("and 1 more team member"). A
member whose age Weave doesn't say counts as under 18 here, since the
gallery is public. New site_settings table (one row) for settings that
aren't compute policy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/compute/ospool.md: integrate as a gateway (Patchwork is the only identity OSG sees), the three gates (egress, eligibility, automated submission), the access-point routes, identity and minors (one public ProjectName, pseudonymous ids in job ads), budgets via HTCondor GPU-hours at a shadow rate, the mapping onto the provider seam with corrected HTCondor states and holds, the Krater changes it needs, a no-egress fallback, testing, a plan, questions for OSG, alternatives, and an egress test job. providers.md now summarises and links it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the egress test Spend accrues at an instance's hourly rate and the cap checks read the offer's, so $0 offers would leave every ceiling and alert silent until reconcile. htcondor2 doesn't spool by default, so a remote worker must submit with spool=True and upload the runner and token. The egress test now signs its PUT for the exact size, defines its URLs, records the site through job_machine_attrs, and says which failures mean blocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ly Krater's own answers A box confirmed /workspace as saved even when the push failed, and a job reported its exit after a failed final upload, so outputs could be lost behind a success. Both now retry for a bounded time; a box stays unconfirmed (Krater records it unsaved at its sync deadline) and a job's log says what is missing. Any 403 or 4xx from a proxy in front of Krater (an HTML WAF page, Funnel) no longer ends a job or resets the dead-man switch: only 2xx or Krater's JSON answers count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t, revoke on logout - wait and logs -f retry 5xx/429/timeouts with backoff instead of exiting 1 like a failed job; - download refreshes its signed URLs after a 403, re-downloads same-size files unless --skip-existing (checkpoints keep their size), and skips dot segments or unwritable names instead of crashing; - non-finite prices are refused; job log text is stripped of terminal control sequences (--raw opts out); - a refresh retries with a token another invocation rotated, and tells a Weave outage from an expired sign-in; the credentials file is written through a unique temp file; - logout revokes the tokens at Weave before deleting them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ry IDs, input 500s - The scheduler re-reads a job under a row lock before renting for it or requeueing a moved one: a cancel landing mid-pass was overwritten, and a box cancelled while moving rented a new machine. - A self-stopped job's machine start is retried every pass (Vast never resumes a deliberately stopped machine). - The public gallery leaves unnamed anyone Weave doesn't return, instead of printing their Patchwork ID. - Job pages and file downloads check Weave live (cached directory answer on GET), not the roles from sign-in. - Accepting a team invitation takes the project row lock grants take, so a join can't race past the guardian gate. - Storage names accept what ML tools write (epoch=3-step=100.ckpt) and refuse . segments. - The label prefix must be specific (the sweep destroys unknown instances with it). - API: non-finite prices, non-string commands and huge GPU lists are 422s, not 500s; estimator too. - A blank repo or demo link clears it; reconcile keeps the accrual mark when the estimate stands; money ceilings use integers; the watchdog keeps destroying through Vast errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s for Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…offer_id, provider_machine_id Vast's ids are integers, but AWS's (i-0abc...) and HTCondor's (1234.0) aren't. compute_instances' vast_instance_id, offer_id and machine_id become strings named for any provider (alembic c8f2a4d6e1b3; last_vast_status becomes last_provider_status; the per-provider unique constraint takes the naming convention's name). The downgrade refuses while a row holds a non-integer id. Checked up, refused, down and up again on a copy of staging's data. The provider protocol and the Vast shapes carry string ids; krater.vast turns them back into integers at the wire and refuses anything but plain digits, so an id can never change a request path. The runner's /start keeps its vast_instance_id integer for runners already deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in /start Built from the provider's self_stop_method (vast_api or shutdown), never from its name. The instance id is a string. The legacy vast_instance_id and vast_api_url fields are still sent, and a runner without a descriptor uses them as before. An unknown method or an unusable id stops the command but leaves the machine running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
krater.providers.Phase (provisioning, running, interrupted, stopped, gone) replaces the scheduler's reads of is_running, intended_status and cur_state. krater.vast maps its statuses to a phase, plus stopping for the lag after a stop; a table test covers the combinations seen on staging. Vast behaves as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scheduler's and the reconciler's reasons and events said Vast; they now take the active provider's title. Quilt's spend-correction reason names no provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without one, a runner told to stop itself would fall back to Vast's API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Vast's krater.compute.launch builds a neutral Launch (image, disk, label, bootstrap, box, bid, env); ProviderInfo.create_request turns it into the provider's create request. Vast's args mode with sh as the entrypoint, and its ssh_direct mode for boxes, now live in krater.vast.launch. The request Vast gets is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-shaped Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Infra's Krater module no longer runs krater-db or a backup timer: the database is the schema `krater` on skein, written by bin/new-krater-secrets, and skein's nightly dump is the backup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jaspermayone
force-pushed
the
claude/application-form
branch
from
October 22, 2026 04:15
16de89a to
d6e5a38
Compare
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title: Krater Compute, the application form and project teams
Base:
main← Head:claude/application-form(patchworklabsorg/krater)mainholds only the initial commit, so this PR carries everything on the branch:claude/exciting-sagan-7oh2zh: proposals, review, budgets, Slack, the Quilt outbox, Jasper's UIredesign, and refactor: stop storing copies of member data from weave #7, which stops Krater storing member names and emails);
claude/krater-compute);If PR #1 merges first, this diff shrinks to the compute and form work.
What the branch adds
verified guardian in Weave. Joining a funded project runs the gate too.
krater/providers): what Krater can call on a provider.claude/provider-neutral):provider_instance_id,provider_offer_id,provider_machine_id.Phasereplaces reads of Vast's status fields.Launch, and each provider turns it into its own create request./startgives the runner aself_stopdescriptor (vast_apiorshutdown); the legacy fields are still sent.ProviderInfo.title.runner_self_stop_minuteswith no answer from Krater (default 3 h,overridable per project), the runner stops its own machine through Vast's API with the container's own key. Krater,
once it is back, starts a job's machine again or shows a box as stopped.
cli/,krater): sign-in through Weave (PKCE), submit (including--limit), list, logs andlogs -f,wait, download, ssh, stop/resume/cancel, secrets.Migrations, in order (Alembic, single head
c8f2a4d6e1b3)PR #1's chain up to
83af68b415ca(quilt outbox), then:a7c1e5d9f2b4: stop storing member data from Weave (refactor: stop storing copies of member data from weave #7)f736452b1d88: compute settingsa4c9e2d71b03: GPU price samples23eb52c373d1: compute jobs, instances and eventsb81f0c6d2e57: compute spend estimateb9a50b19751b: spend reconciliationsc2d8e5f4a910: compute runnerd5e1a7b3c204: compute storage quota4cd4032b89cb: compute boxese7a2c9d1f305: compute run secondsf3b8d4e60a12: drop SkyPilot columnsa90b9c697fd0: project secretsb6d3f9a1c2e7: cancel queued skypilot_reconcile tasksc4e8a2f7b913: vast_search_quotab2d4f6a8c0e1: merge point (refactor: stop storing copies of member data from weave #7 into compute)e3a9c6b2d714: project_members; dropscredited_builder_ids(credited builders become accepted members)f5b2d8e1a936: the application form's answers2d64a8c637d2: runner self-stopefcb0ce55d26: limit ordersa7c3e91f0b42: compute instance providerb5d1e7a3c920: site settings (gallery minors)c8f2a4d6e1b3: provider ids as strings (its downgrade refuses while any id isn't an integer)How it was tested
go vet,-race).ruff checkandruff format --checkare clean. There is one Alembic head.self_stopped, paused the job and started the machine (200). The runner reported again within seconds.a job and a box rented through the new launch path, the CLI, and
/startreturning the descriptor.Open items
the Vast key is rotated (then
compute.enable). Its database is the schemakraterin patchwork_central on skein, not aPostgres of its own (
docs/dev/production.md0.1). The DNS records are in feat: add krater and krater-storage.patchworklabs.org dns#12. After this merges,set infra's fallback
revto the merge commit.krater-project-secrets-key.age, portal only), but the key isn't made yet.e3a9c6b2d714migration turns credited builders into accepted members without consent or a guardian check, and its downgrade drops them. This doesn't matter while production has no projects.KRATER_TRUSTED_PROXY_COUNTbehind Cloudflare and Traefik.second provider.
🤖 Generated with Claude Code