Bump node from 22-bookworm-slim to 26-bookworm-slim in /web - #3
Closed
dependabot[bot] wants to merge 932 commits into
Closed
Bump node from 22-bookworm-slim to 26-bookworm-slim in /web#3dependabot[bot] wants to merge 932 commits into
dependabot[bot] wants to merge 932 commits into
Conversation
The clone source is resolved as a Volume so a cloned Environment is placed at all; `settle` gets the no-op guard every other status writer has, so a permanently-broken object stops re-stamping its own condition; a terminating stop request no longer reads as a landed push; and stopping a workspace no longer waits on a disk it does not need.
The Snapshots page has no durable index today: it enumerates live Workspace and
Environment objects and joins snapshots onto them, so a volume whose parent was
deleted is invisible even though its records and blobs are all still there.
The records already live on the server tier at vol/{owner}/{name}. What was
missing is a user-facing way to read them: /vol-agent/* is per-volume only and
authenticates a region's agent token, which is not an authorization answer for
"may this person see these snapshots".
Two routes, each following the shape its scope demands. `volumes` is
owner-scoped like `images` and reads the shared object store alone -- one LIST
of repo/vol/{owner}/ names every volume without opening any of them, which is
what makes it safe to serve on whichever node receives it. `volumehistory` is
routed by the volume key like `imagetags` is by the image key, so it opens the
database only on the node that holds it.
Dating the list from object mtimes is deliberate and approximate: a compaction
can run ahead of the last real push. It is enough to sort and date a list by,
and volumehistory has the exact times.
The Snapshots page listed live Workspace and Environment objects and joined snapshots onto them, so deleting a workspace hid every snapshot ever taken of it -- and restore called my_ws() on the source first, so those snapshots could not be restored either. Both followed from treating a live parent as the index. A snapshot is a point in time. Its record and its bytes live on the server tier and outlive the workspace, so that is what /v1/volumes, /history, /refs and restore now read, through the browse routes over the peer credentials this process already proxies browse reads with. SnapshotRequest goes back to being only the push work item: nothing user-facing reads one, so a collected request costs nothing. The cluster is still asked one question -- is the parent still there -- and its answer only sets `deleted` and names live rows. A kube outage degrades the page to bare ids rather than emptying it. Provenance rides in the commit record's state, written by the agent at push time from the Volume's ownerReference, because once the workspace is gone that is the only thing left that can say what the snapshot was of. Records without it fall back to the volume id, which is what the page showed for everything before. Restore now resolves a snapshot id against the caller's own histories and takes the volume from it. Quota comes from a live source when there is one, else the body, else the fallback; src_workspace is accepted and ignored so a web build from before this change survives a roll.
The list rows now carry what the source was called, whether it still exists, and when it was last written, so a volume outliving its workspace reads as "api-scratch, source deleted" rather than vanishing from the page. Restore moves to every row and sends only the snapshot id. It no longer names a source workspace, because the case that matters most is the one where there is none; the restored workspace takes the standard quota then, with no field to fill in -- someone recovering a lost workspace is not sizing a disk. The detail page drops the kind query param it used to gate restore on. It reads by volume id, which needs no live parent.
A stream layer larger than 120 s of link bandwidth could never be restored:
`get_bytes` bounded the whole body with one deadline and every fetch error
carried the FETCH_FAILED marker, so the Volume settled Error/FetchFailed for
good. The deadline is now per chunk (the block path already did this) and
only a NotFound/PermissionDenied/Unauthenticated answer is permanent; a
timeout or 5xx comes back unmarked and is retried.
An agent restart mid-stop left the fixed-name `stop-{env}` request at
error/AgentRestarted, and with no /v1 delete for requests the environment
stayed parked until kubectl. That error is the agent's, not the push's, so
`await_stop_push` deletes it and creates a fresh one; a real PushFailed
still parks, and nothing is torn down until a push has landed.
…team website scheme
The emailed /verify/{token} link was a GET that redeemed the token and set the
session cookie, so anyone who could make a browser open a URL could sign it into
an account of their choosing. The page is now inert; a Server Action behind a
Continue button does the redeem and signIn, with next re-validated through
safeNext on the way through.
A team website now has to be an http(s) URL on both tiers: the api refuses
anything else with 400 (and caps the length), the settings form reports it as a
field error, and the public profile renders an already-stored bad value as text
rather than an href.
…arkers The upload sweep aged a session by its staging object, which the fast path writes empty at open and never touches again, so any push longer than the grace window was swept out from under the client. Age a session by the newer of its two objects, and abort the sidecar's multipart upload before deleting it so its parts do not outlive the session in the bucket. The bucket-side rule the code kept citing now exists, in deploy/README.md. The marker reconcile compared the owner's clock stamp against the object store's second-granular mtime for exact equality, rewriting every marker once after every push. A gap only a missed push could open counts now.
…ound the feed fan-out and refuse traversing volume names
check_repo capped rows rather than work, so the same 25 lowest-numbered
open changes filled the cap on every pass and #26 onward were never
checked. merge_base collapsed a budget-exhausted walk into "unrelated",
which the sweep recorded as Dirty and the page read as no merge button.
/v1/activity asked each owning node in turn on the 15 s client timeout;
it now asks four at once under a 5 s deadline and returns what answered.
/v1/volumes/{name} and {snapshot} were spliced into peer URLs unchecked;
they now pass the same valid_segment rule as created names, or 400.
App::route answered Local for a repo whose object-store prefix was still empty, so the first write to a new repo, image or volume opened its database unleased, and every other node saw the same empty prefix and opened it too until the manifest landed. Route on the ownership map alone: a name the map does not know is claimed first, and a name that really does not exist still 404s in the handler without opening anything. This also puts api_create under its lease, since the middleware claims before the handler runs. The negative cache existed only to make the prefix probe cheap, so it goes with it. Q-20 (delete racing a concurrent open) is untouched.
… on disk Q-18: an environment's stop snapshot is taken after its StatefulSets are at zero and their pods are gone, not under running databases; the deletes still wait for the push to land. Q-52: a stopped environment that is restored is observed at the new generation instead of pushing the restored subvolume as an unrequested commit. Q-36: a claim leaves an object alone when the owner's OwnerBinding names another node. Q-37/Q-38: a failed send deletes the RO snapshot it took, the janitor sweeps unnamed recv/ subvolumes by their own creation time, and the auto-squash child is reaped. S-10: spec.quotaGb is a btrfs qgroup limit on the live subvolume, re-applied on every materialize and restore; a pool without qgroups surfaces QuotaEnforced=False instead of being silently uncapped, and format-pool.sh enables quotas on new pools.
…aming clients for disk faults The agent ClusterRole is now the table at the top of agent-rbac.yaml: every kube call the controller makes, one row each, and no verb outside it. `secrets` leaves the ClusterRole entirely — the host-key grant is bound per `ws-*` namespace by the binding reconciler, the same shape as the api's — and `events`, `nodes list/watch`, `update`/`delete` on kinds the agent never edits or deletes are gone. What RBAC cannot express (field-level patch, who a `bind` may be used for) is a ValidatingAdmissionPolicy in agent-admission.yaml: the agent may change nothing in spec but `Volume.spec.restoreTo`, and may only write `ws-*`/`env-*` namespaces, `ws-ssh-*` Secrets, and the two api/agent secret RoleBindings. CLAUDE.md, README and crd.rs now say what the deployment actually enforces. `/healthz` also requires the leader to have answered within one LEASE_TTL, cached from the renew beat (which no longer skips an idle node), so a freshly rolled pod is not Ready before it can claim. Server-side io errors are 500 again: `is_client_fault` matches io kinds client input produces rather than the io type, a Basic header that does not decode is refused on browse instead of degrading to anonymous, and a connectivity walk propagates anything but a missing object rather than telling the pusher their pack has holes.
A manifest GET by tag did a durable SlateDB put under a per-tag lock before answering, so concurrent pulls of one tag queued behind a WAL flush each. The counter is display only: tally it in a per-node map, fold it into the image's database on the owning node's 30 s lane with await_durable off, and accept losing one window to a crash.
Every fetch with a have and every push built the full object closure of the repo (`reachable_set`) to answer "does this repo have X". A have is a commit, so a commit walk from the tips that stops at the last target answers it; an up-to-date fetch now costs one lookup. A push walks only the trees the client sent, explains an unchanged subtree by the trees of the commits the new history grows from, and pays for the whole-repo closure only for an object neither explains. The gix#2935 merge workaround in pack.rs is untouched. `upload::WALKED` counts the objects visited so tests/walk_cost.rs can pin the cost: a 1200-object repo now walks 2 objects on an incremental fetch and 7 on a push of one commit, where both used to walk all 1200.
The upload-pack reply now leaves as the pack is built: the protocol writes into a channel-backed writer that holds back the first 64 KiB (so a fence can still be retried and an error can still set the status) and streams the rest as a chunked body, bounded by the channel so a stalled client stalls the build rather than growing the pod. The receive-pack body feeds gix-pack's indexer straight from the request stream; an unlinked spool under the pack dir keeps the bytes for the in-flight fence replay and is capped by max_body as it streams. The receive semaphore, auth-before-body ordering, upload-pack 413 and the merge-commit pack workaround are unchanged.
Every push added a pack and nothing ever removed one; the only repack was the admin command with the server stopped. The owner's lanes now fold a repo's packs back into one once it holds more than RUSTIC_GIT_REPACK_PACKS (default 32) of them. The online shape copies every object of the packs it listed rather than rebuilding from the refs: pushes are not serialised against each other, so a rebuild from the refs could drop a push's objects in the window between its pack upload and its ref update. The admin path keeps the ref-based rebuild (which also collects garbage) over the same tail. Both retire an old file from the pack index before the object store, so a crash leaves duplicates rather than an index row naming a missing pack. The repack lock is in-process now: the DB key outlived a crashed run and blocked every repack after it.
The audit's I-4: the invariant violation, claim outcomes, 421s, merge outcomes and reconcile errors were all kubectl-logs-only, and the log lines could not be queried on a field. One recorder and one JSON switch in rustic-git-core so the binaries cannot drift: RUSTIC_GIT_LOG_FORMAT=json turns the shared subscriber into one object per line, and the peer router (server) or a RUSTIC_GIT_METRICS_ADDR listener (api, worker, agent, gateway) serves /metrics. Never a public listener: the scrape text enumerates repository keys. Counters: HTTP count and latency by listener, route class and status (413 and 421 named), git pack requests and bytes in, registry blob bytes in/out, db_fence_detected_total at App::on_fenced where every path lands, ownership map size and claims (granted/moved/heldby), renew failures, merge outcomes and duration, reconciles per kind and result, open gateway tunnels. deploy/: prometheus.io scrape annotations (no Operator assumed) and deploy/alerts.md with the PromQL an operator should set.
The image job now needs both build and test, so a red commit has no package at all — not :latest, not :<sha> — and the deploy side can read "the tag exists" as "the tests passed". Tests also run on pull requests; build and image stay push-only. cargo runs --locked like the Dockerfile, every job has a 30 minute ceiling, both workflows that run bun install or a release start read-only, cross is a fixed version, kl releases carry a provenance attestation, a weekly audit covers cargo advisories and bun, and Dependabot moves the SHA pins.
Bumps node from 22-bookworm-slim to 26-bookworm-slim. --- updated-dependencies: - dependency-name: node dependency-version: 26-bookworm-slim dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Author
|
Superseded by #14. |
dependabot
Bot
deleted the
dependabot/docker/web/node-26-bookworm-slim
branch
September 4, 2026 15:55
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.
Warning
Cooldown could not be applied because no publication date was available from the registry.
Bumps node from 22-bookworm-slim to 26-bookworm-slim.
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)