Response to "Scion and sgit" (newsroom.sgit.ai, article No. 141, v1.0.0)
Date: 16 Oct 2026. Code read: dev at 881d6df (what 0.22.0 ships plus the 14 Oct fixes). Vault measured: the comms / DC collaboration vault (y8pwtjlw on dev.send.sgraph.ai), read-only: cloned eight ways from the hosted API, then the same objects served from local disk (static and a local API server); nothing pushed to the real server. Also measured: the vault the article used (newsroom-synthetic-users, bg1opz3c, public read key), from the same kind of sandbox. Also read: Anthropic's post in full, every sgit.ai page the article should link, and the eleven newsroom articles that already describe sgit's architecture and deployment.
Verdict
The Scion half is accurate: every figure the article quotes is in Anthropic's post. The sgit half is under-claimed where it should be strong (deployment models, reading files without a clone, the pull fix, history integrity) and over-claimed in one place (a CDN that caches for a year), and its measurement is the wrong one: an 83-file vault says nothing about the question the post raises, which is what happens when the repository is big and the clients are many. We have a vault that is big by sgit's standards and is used by ten agents every day. Its numbers are below, and they are the ones the article should carry.
Three things the article gets wrong on the facts, in order of how much they matter:
- "no packing on the server" is listed as a gap. Scion has no packfiles either and says so as a feature: "There is no equivalent of packfiles … so no repository maintenance is needed." sgit is the same: immutable objects, no maintenance, no GC on the server. This is a point of agreement, not a gap.
- "its latest releases add scoped, shallow and on-demand clones" / "sgit 0.18 to 0.22 added …
--sparse".--pathand--depthare 0.18.0 (7 Oct).--sparsehas been in the CLI since v0.11.3 (1 May 2026). - "objects cache for a year at a CDN". Drop it (see §2, claim 1). The property is that they can be cached by anyone, for ever, and the evidence for that is what the sgit.ai pages do in the browser, not a CDN header.
1. What Scion actually says, and the overlaps the article missed
Read against the post, the article's Scion paragraph is right (2,500 → 30,000 merges a day, ~100 GB of main-branch history, ~25 M CI checkouts a day, ~600 PB if fetched naively, several hundred mirrors, three cache tiers each over 99 %, 200× less CPU and memory, seconds instead of minutes, 25 % of CI's CPU, seven people in five months, "by no means a suitable replacement for git"). Four things in the post are closer to sgit than the article noticed:
| Scion (from the post) | sgit | Where it is documented |
|---|---|---|
| "Scion's negotiation doesn't tell the server what the client already has; instead, it operates exclusively on trees and blobs the client wants. Starting from a root tree, the client checks its local store for missing trees and requests them recursively, then repeats the process for blobs." | This is sgit's clone and pull walk, word for word: level-by-level over the trees, one batched request per level, then the blobs in batches of 50 with 16 requests in flight. A scoped clone asks only for the spine down to its folders. | docs/partial-clones, CHANGELOG 0.18.0; Step__Clone__Walk_Trees, Vault__Scoped_Tree.walk_fetch |
| "Every request is for a specific object that never changes, so any cache can answer it." | Every object's name is the SHA-256 of its ciphertext; the client checks every object against its name before writing it. The mutable ref is no-store. | api/vault-objects (the caching contract), case-studies/live-vault-docs |
| "There is no equivalent of packfiles … no repository maintenance is needed." | Same. No packing, no GC, no maintenance on the server. For cold starts from a static host, sgit publish --bundles emits a head snapshot and per-commit delta zips. | CHANGELOG 0.18.0 (static publishing) |
| "moving [a ref] is a compare-and-swap on a single Spanner row" | Refs move by compare-and-swap; so does the branch index, which every client merges as a shared document. | docs/history-integrity, docs/vault/vault-from-javascript §5 |
And two differences the article should state, because they are the real ones:
- A Scion changeset stores the list of files that changed; an sgit commit stores a tree, like git. Scion rebuilds history and blame as "derived data" on the server. sgit's tree snapshot is what makes a scoped clone cheap (an unchanged folder keeps its tree id across commits, so it is never fetched twice) and what makes a client-side blame possible without the server.
- Scion protects directories with permissions on the server; sgit protects them with keys. A partial clone is a speed setting, not a boundary (
docs/limitations); the boundary is one vault per trust domain, a read key that cannot be turned into a write key, and append lanes that can write but never read. The article says nothing about any of this, and it is the strongest contrast with a server that holds plaintext and an IAM system in front of it.
2. Claim by claim
| # | The article says | Status | Say instead (evidence) |
|---|---|---|---|
| 1 | "Its object ids are immutable content addresses, so objects cache for a year at a CDN" (title paragraph, abstract) and "the live server already uses it: … cache-control: public, max-age=31536000, immutable, through a CDN" | Half true, and the half that is true is the wrong half. The header is now sent (verified today on /api/vault/read/y8pwtjlw/bare/data/obj-cas-imm-…: public, max-age=31536000, immutable; the ref answers no-store). But three consecutive reads of the same object all came back x-cache: Miss from cloudfront: the CDN in front of the dev API does not serve reads from cache today. | "Objects are named by the hash of their ciphertext and the name carries -imm-, so anything that has fetched one can keep it for ever without reading it: a browser's Cache API, a proxy, a CDN, a folder on disk. That is how the browser app is fast: it caches every -imm- object on sight and refetches only the ref. sgit.ai's own documentation pages read a vault that way: a cold load fetches 18 objects, the next load fetches one 69-byte ref, and a load inside the two-minute freshness window fetches nothing. The price is that sgit keeps no deltas: a changed file is a new object." Link case-studies/live-vault-docs and docs/vault/reading-a-vault-file. |
| 2 | "measured it on a real vault: a scoped clone in 4 seconds against 59 for the full one" (12 MB, 83 files) | Wrong vault, and the 59 s was a bug, now fixed. 83 files answers nothing about Scion's problem. The 59 s reproduced exactly from here (59.0 s, 56.5 s): the vault's 106 objects go up in two batches of 50, each batch's response is over the Lambda's size limit, the client slept 14 s retrying each whole and then read 106 objects one at a time. A refused batch is now split in halves on the first refusal: 8.1 s and 9.9 s for the same clone (§3). The article measured a Lambda limit and a client fallback, not the vault. | Use the comms vault (§3): 35k objects, 1,295 commits, 5,281 files, ten agents writing daily; and say what the 59 s was. |
| 3 | "sgit 0.18 to 0.22 added the same family of options: --path …, --depth …, and --sparse" | Partly wrong. --sparse is from v0.11.3 (May). 0.18.0 added --path and --depth; the 14 Oct fixes made clone-headless + fetch/cat/ls work. | "sgit has had on-demand (--sparse) clones since May; 0.18.0 added a folder scope and a history depth, and they combine." |
| 4 | "writes … go through a batch endpoint, up to 100 operations at a time" | Server cap is 100; the client sends 50 per request with 16 requests in flight on reads. | "batches of 50 objects a request, 16 in flight". |
| 5 | "a vault of 626 commits and 9,404 files" | 9,404 blobs; the file count was not recorded then (today's vault: 5,101 files, 16,315 blobs). | "9,404 blobs". |
| 6 | "The CLI team measured a scoped clone of one folder in 14 seconds against 81" | True (6 Oct, 18,720 objects). | Keep, but it is superseded by §3. |
| 7 | "sgit does no packing or compaction on the server" under Where Scion is ahead | Not a gap (§1). | Move to the overlaps. |
| 8 | "a vault of 35,000 objects still takes about three and a half minutes to clone in full" | True (14 Oct), and it is the honest number for a whole vault. What the article leaves out: nobody doing agent work needs the whole vault, and the pull that an agent does every session was the slow path, fixed on 14 Oct (§3). | Keep the 3½ min and put the scoped/depth numbers and the pull fix next to it. |
| 9 | "sgit has a history log on the client but no blame and no bisect" | True. Understated: history log --file PATH (path history), --stat, --grep/--since/--until/--author, history diff A..B, history show, history revert --as-commit, commit --amend, history undo, reflog, vault tag (signed), and check verify (which commit was signed by which key). Blame is a client-side fold over path history and nothing in the format prevents it. | "No blame or bisect yet; path history, signed authorship and signed tags, yes." Link docs/history-integrity, docs/update-to-0-21-0. |
| 10 | "There is no command to read a file from a vault without at least a headless local clone" | True for the CLI, and misleading: reading one file is four GETs by design (ref → commit → trees on the path → blob), which the site's pages do in the browser with a 90-line reader, and clone-headless is a config directory, not data. The real edge is in §3: on a long history today's cat first walks the commits. | "A file is four GETs away from its read key, and sgit.ai's pages read vaults that way; the CLI needs a headless clone directory first." Link docs/vault/reading-a-vault-file, docs/vault/vault-from-javascript, demos/vault-app-embed. |
| 11 | "Merges happen on the client, three-way and whole-file; Scion does them on the server" | True. Add: two independent clients (the CLI and the browser vault app) speak one wire format, so a merge made in a browser is one the CLI verifies and vice versa (sgit serve pull). | Link docs/what-is-sgit (three doors), security/ (two implementations, one wire format). |
| 12 | "Git and sgit also live side by side rather than in sync … no bridge" | True for history. There is vault attach (bind a key to a .sg_vault/bare that came in through git clone), the one-repo pattern, and a pure-Python git reader for vault apps. | Link docs/vault/git-and-vaults, case-studies/one-tree-two-remotes. |
| 13 | "The server only matters for writes" | Also for the store listing that makes a cold full clone fast (one list, then bulk fetch). A static host has no listing, so a cold full clone from one walks the graph; a scoped or depth clone never lists. | Fine as a simplification; say "writes and the branch index". |
| 14 | Side-by-side figure "Scion, sgit 0.22.0 and Git … proven scale and availability" | Add rows: server holds plaintext (Scion yes, sgit never), directory protection (Scion IAM, sgit keys per vault and read-only / write-only keys), clients (Scion one, sgit two implementations), deployment (Scion internal fleet; sgit hosted, your container, static host, a folder). | |
| 15 | "What it means … The useful next steps are clear from Scion's list: server-side history queries that do not need the plaintext" | A server that cannot read commits cannot answer history questions; that is the design, not a roadmap item. The sgit answer is a client-side index over a scoped clone, which §3 shows is small. | Reword: "history queries over a scoped clone on the client, and a commit-chain object so a partial clone does not walk history one commit at a time". |
| 16 | Where this comes from: "the largest vault sgit's team documents has about 35,000 objects" | Make it concrete: it is a live vault with ten agents, 1,295 commits (68 added in the last two days), 5,101 files, 527 MB of ciphertext in a full clone. |
3. The evidence the article should carry
All timings from one client in this cloud sandbox (every request leaves through a proxy; a plain connection does better), against dev.send.sgraph.ai (S3 storage behind CloudFront), sgit at dev 881d6df (0.22.0 plus the 14 Oct fixes), one run each unless stated. The vault: 1,295 commits on 16 Oct (1,227 on 14 Oct), 16,391 trees, 16,315 blobs, 5,101 files, 35,423 objects / 527 MB in a full clone on 14 Oct.
Clones (the full clone is the 14 Oct run; the vault has grown by 180 files since):
| Clone | Time | Objects | Store on disk | Files checked out |
|---|---|---|---|---|
| everything (full clone) | 3 min 30 s | 35,423 | 527 MB | 5,101 (131 MB) |
--depth 1 (whole tree, one commit) | 57 s | 6,248 | 142 MB | 5,281 (139 MB) |
--path security --depth 1 | 13 s | 137 | 3 MB | 130 (3 MB) |
--sparse --depth 1 (structure only, one commit) | 27 s | 967 | 6 MB | 0 |
--path docs (12 files, all history) | 181 s | 2,668 | 17 MB | 12 (1 MB) |
--path security (all history) | 227 s | 3,364 | 64 MB | 130 (3 MB) |
--path mail/crm.riskmandate (all history) | 159 s | 6,109 | 69 MB | 558 (27 MB) |
--path mail (the biggest folder, all history) | 264 s | 25,110 | 274 MB | 3,275 (69 MB) |
--sparse (every commit and tree, no blobs) | 206 s | 18,299 | 117 MB | 0 |
clone-headless (keys and config only) | 0.4 s | 0 |
Working in a partial clone (each is one CLI invocation, so ~1.5 s of it is process start and PBKDF2):
| Operation | Time | Network |
|---|---|---|
ls docs in the sparse clone | 1.8 s | none (trees are local) |
fetch docs/drafting-guide.md into the sparse clone | 2.9 s | 1 object |
cat of a held file | 1.7 s | none |
cat of a file not yet held (fetched on demand) | 2.5 s | 1 object |
fetch security/ (130 files) into the sparse clone | 5.1 s | 130 objects, batched |
fetch security to widen the --path docs clone | 7.8 s | 135 objects |
status in the scoped clone, up to date | 1.5 s | ref + index |
pull in the scoped clone, up to date | 2.5 s | ref + index |
cat of one file from a headless clone (first command: brings every commit and tree down) | 202 s | 18,300 objects, 116 MB |
cat of a second file from the same headless clone | 2.5 s | 1 object |
history log --file <path> on the full clone (1,227 commits) | 6 min 8 s | none: CPU, 5½ min of it |
The article's own vault (newsroom-synthetic-users, 83 files, 12 MB, public read key), cloned from this sandbox for comparison with its "59 s full / 4 s scoped":
| Clone | Before the fix (0.22.0) | After (dev, batch splitting) | Objects / store |
|---|---|---|---|
| full, read-only | 59.0 s, 56.5 s | 8.1 s, 9.9 s | 106 / 12.4 MB |
--depth 1 | 62.1 s | (same path) | 93 / 12.0 MB |
--sparse | 4.0 s | 18 / 180 KB | |
--path <first folder> (5 files) | 3.7 s | 15 / 412 KB |
The article's numbers reproduce to the second. The full clone's 59 s was two 50-object batches whose responses exceeded the Lambda's size limit (files of ~150 KB): the client retried each whole through the 2 + 4 + 8 s back-off and then read 106 objects one per request. Fixed on dev (CHANGELOG, Unreleased): a refused chunk is split in halves on the first refusal.
What the numbers say
- A scoped clone of a 35k-object vault is a few MB and a few seconds, when it also carries a depth.
--path security --depth 1and--depth 1are the agent pattern the docs already recommend ("an agent clones only its own folder with one commit of history"), and they do not grow with the vault's history. - Without a depth, a partial clone pays one round trip per commit.
--path docs(12 files) took 181 s and 2,668 objects because it walked 1,295 commits one request each, then the spine trees of every one of them (that is what giveshistory logon a scoped clone);--sparsewithout a depth is the same walk (206 s),clone-headless+catpays it on the firstcat(202 s, then 2.5 s a file), and--path mailwith all its history is most of the vault (25,110 objects). Whole-vault clones and pulls stopped doing this on 6 and 14 Oct (list the store once, fetch in bulk); a partial clone cannot list-and-sweep because it must not hold what it does not track. This is the one real performance edge today, and it has a clean fix that needs no server change: a small encrypted "commit chain" object written on push, so a clone batch-fetches the chain (1,295 commits is 26 requests, 16 in flight) instead of discovering it. Until then:--depth. - Once a clone exists, every operation is a few requests. A file on demand is one object and under 3 s including process start; a folder of 130 files is 5 s; a pull that is up to date is 2.5 s. This is the "fetch only what you need" number, and it does not depend on the vault's size.
- History queries on the client are the other honest edge.
history log --fileover 1,227 commits took 6 min of CPU on the full clone because it flattens every commit's tree; it should skip subtrees whose id did not change, the way the clone walk does. The fix is local and needs no format change. - The pull an agent runs every session was the slow path, and is fixed. Fetching the whole head into an empty store through the pull path: 186 s now, about 53 min before 14 Oct (one request per commit and one per blob). Measured on this vault, in the 14 Oct note.
- A full clone is the worst case and still bounded: 3½ min for 35,423 objects / 527 MB (listing 21 s, bulk sweep ~140 s, local tree decrypt 33 s, checkout 4 s).
- Server-side numbers already published: ten scoped clones at once in 1.9 s against ten full clones in 21.1 s on a single server process (the single uvicorn worker is the limit, not the protocol); a one-folder clone of 0.59 s at 100 files and 0.77 s at 2,000 while the full clone went 0.95 → 5.55 s; 100 writers landing 1,000 append-lane entries in 4.9 s with none lost (newsroom: Agent workflows on ephemeral vaults, sgit in GitHub Actions, side by side).
4. The argument the article should make
Scion's post names what matters at agent scale: always-online clients, fetch only what a job needs, immutable objects any cache can serve, cheap incremental commits, a sparse working copy by design. Point by point, with the mechanism and the number:
- Fetch only what you need. A clone asks for the spine down to its folders and nothing else; unchanged folders keep their tree ids across commits, so they are never fetched twice. §3.
- One file is four requests, from any host that answers a GET. Ref → commit → trees on the path → blob, computed from the read key; no listing, no index, no account. The site's own pages do it;
sgit caton a sparse clone does it (and, once a file is held, reads it with no network). - Immutable objects, cached by whoever fetched them. Browser Cache API on sgit.ai, a CDN,
--transport staticfrom GitHub Pages or S3 or a folder,sgit vault mirrorfor custody without access. The server sendsimmutable; the design does not depend on it. - Writes are one batched request and a compare-and-swap. 50 objects per request; the ref and the branch index move by CAS, and every client merges the index as a shared document. A commit costs the size of the change: the new blobs, the rebuilt spine, one commit object.
- Many clients, a dumb server. The server computes nothing, so it scales as storage does (S3 behind CloudFront is the hosted deployment;
sgit serveis the same image on a laptop). Append lanes let a hundred writers post without reading. - And the thing Scion cannot offer anyone outside Anthropic: the server never holds plaintext, the read key is one-way derived and publishable, and the whole thing is
pip install sgit-ai.
Said with the gaps: no blame or bisect yet; partial clones walk history per commit until the commit-chain object exists; one server process queues full clones; proven at 35k objects and ten agents, not at 100 GB and 30,000 merges a day.
5. Deployment models the article should claim (all shipped, all documented)
| Model | What runs | Docs |
|---|---|---|
| Hosted | dev.send.sgraph.ai: S3 storage behind CloudFront, access token per account | /docs/installation, /api/ |
| Your own server, one container | diniscruz/sg-send-vault with an access token and a folder or an S3 bucket; Docker Compose on a Mac mini; Kubernetes; a cloud VM with no public route; two servers, one vault | /docs/self-host-for-agents, /deploy/, newsroom Encrypted memory for agents that run somewhere else (five patterns) |
| On the laptop, from the clone | sgit serve: the vault UI + API in Docker, Podman, Apple container, nerdctl, Finch, or uv with no container; serve pull/push move work between the browser and the clone | CHANGELOG (Unreleased, sgit serve) |
| Static host, no server | sgit publish + any GET host (GitHub Pages, S3, a CDN, Python's file server); clients use --transport static; sgit serve --static locally | /docs/vault/static-hosting, CHANGELOG 0.18.0, newsroom The homepage is a vault (317 ms a request via the API, 44 ms from GitHub Pages) |
| A folder | --transport local | CHANGELOG 0.18.0 |
| CI sidecar | the server as a job sidecar; ciphertext in the cache or a branch between runs; a key per job; append lanes per shard | newsroom sgit in a CI pipeline, sgit in GitHub Actions, side by side (four lanes) |
| In the browser | the vault app, vault-embed.js, and pages on sgit.ai and newsroom.sgit.ai that render a vault with a published read key | /docs/vault/vault-apps, /docs/vault/vault-from-javascript, /demos/vault-app-embed, /case-studies/live-vault-docs, newsroom Two agents, one vault, No server, by design |
6. Website changes (sgit.ai)
/api/vault-objects→ "Measured 21 September 2026: the immutable header is not currently being sent." Stale. Measured 16 Oct:cache-control: public, max-age=31536000, immutableonobj-cas-imm-*,no-storeonref-pid-muw-*. Add: CloudFront still reports a miss on repeated reads of the same object (the token header is likely in the cache key), so the CDN is not yet a cache for API reads; browser and client caches are./docs/partial-clones→ "Measured". Add the §3 table, and a sentence under--depth: on a partial clone the history walk is one request per commit (a scoped clone of a 1,295-commit vault: 181 s without a depth, seconds with one); whole-vault clones and pulls list the store and fetch in bulk since 0.18 / 0.22./docs/limitations→ "Commit author attribution is not yet recorded … population is planned work." Stale since 0.19.0: commits are signed,check verifyclassifies every commit,history log --authorfilters by key id,vault tagis signed. Replace with the current edges: no blame or bisect, partial clones walk history per commit, no rebase or cherry-pick./llms.txtquick answer "No for: … tags" and/docs/sgit-for-git-users: tags exist (sgit vault tag create/show/list/delete, signed). Also addhistory undo,commit --amend,history revert --as-commit,reflog./docs/self-host-for-agents§1:pip install sgraph-ai-app-send "mcp<2"→ 0.34.0 capsmcpitself; imagev0.33.0→v0.34.0in the three places; the troubleshooting row formcp2.x can go once 0.34.0 is the floor./deploy/: the--endpoint→--base-urlcorrection note can be folded into the vault pages themselves (they are a vault: push the fix).- A single "deployment models" table (§5) somewhere under
/deploy/or/docs/: today the seven models are spread over six pages and two newsroom articles, which is why the article claimed none of them. /docs/vault/reading-a-vault-file: add the CLI equivalent (clone-headless+cat, andclone --sparse+cat/fetch) next to the browser reader, with the history-walk caveat.
7. Links for the newsroom agent
sgit.ai: /docs/partial-clones, /docs/agents-sharing-one-vault, /docs/self-host-for-agents, /deploy/, /docs/vault/static-hosting, /docs/vault/reading-a-vault-file, /docs/vault/vault-from-javascript, /docs/vault/vault-apps, /demos/vault-app-embed, /case-studies/live-vault-docs, /api/vault-objects, /docs/history-integrity, /docs/two-branch-model, /docs/update-to-0-21-0, /docs/limitations, /docs/sgit-for-git-users, /security/, /docs/vault/git-and-vaults, /case-studies/one-tree-two-remotes, /use-cases/ai-agents, /docs/surfaces, /docs/what-is-sgit, /demos/vaults/llms.txt. newsroom.sgit.ai articles: Encrypted memory for agents that run somewhere else, sgit in a CI pipeline, sgit in GitHub Actions, side by side, Agent workflows on ephemeral vaults, No server, by design, The homepage is a vault, Two agents, one vault, The vault is live, Where the vault keys live, From sending a file to sgit, Git for things you cannot put on GitHub. Repo: CHANGELOG.md (0.18.0 partial clones and static publishing; 0.19.0 history integrity; 0.21.0 history commands; Unreleased: sgit serve, the 14 Oct pull fix), and the 14 Oct note team/humans/dinis_cruz/claude-code-web/10/14/response__sgit-issues-2026-10-14.md.
8. Three paragraphs the article can paste
Replacing "Immutable identifiers that cache": Every object in an sgit vault is named by the SHA-256 of its ciphertext, so a name can never point at different bytes, and the client checks each object against its name before it is written. That is the property Scion's three cache tiers rest on, and it means anyone who has fetched an object can keep it for ever: a browser's cache, a proxy, a CDN, a folder. sgit.ai's own documentation pages are a vault read this way: a cold load fetches 18 objects, the next load fetches one 69-byte ref, and a load inside the two-minute freshness window fetches nothing at all. The only mutable object is the ref, and it is never cached.
Replacing "Measured on 0.22.0": The vault this site's agent should have measured is the one ten agents share every day: 1,295 commits, 5,101 files, 35,000 encrypted objects, 527 MB in a full clone. [§3 table]. A full clone is the worst case at three and a half minutes. An agent does not take one: it clones its own folder with one commit of history, in [path-security-depth1] seconds and [MB], and a pull that is a day behind, which used to be the slow path at nearly an hour, now takes three minutes for the whole head. One file is four requests from its read key, from any host that answers a GET.
Replacing "Where Scion is ahead", first sentences: Scion does blame, bisect and merges on the server and builds history indexes as derived data; sgit's server cannot read a commit, so those run on the client, and today sgit has path history, signed authorship and signed tags but no blame or bisect. Neither system packs objects on the server, and both treat that as a feature. Where sgit is behind on its own terms: a partial clone still walks a long history one commit per request (a depth avoids it), one server process queues concurrent full clones, and the largest vault it has been proven on is 35,000 objects and ten agents, not 100 GB and 30,000 merges a day.
9. Storage is the bottleneck, not sgit
The hosted API is a Lambda in front of S3 behind CloudFront. Every request costs its round trip (sandbox → proxy → CDN → Lambda → S3) before a byte moves, and the client's work per object (verify the hash, decrypt) is microseconds. So on the hosted path the numbers are request counts, not bytes: --path security --depth 1 is 137 objects and 3 MB, and takes 13 s because it is about a dozen dependent requests (ref, index, commit, one batch per tree level, the blob batches) plus 0.4 s of PBKDF2. The newsroom measured the same thing from the browser: 317 ms a request through the vault API, 44 ms for the same object from GitHub Pages (The homepage is a vault).
Take the Lambda away and the same objects move at the speed of the storage:
- ruv.sgit.ai ledger (local SG/Send server, one process, disk storage, 11 recorded runs): every sgit operation, including Python start-up and the 600k-iteration PBKDF2, is 0.4 to 1.5 s; a 38-file / 4.0 MB vault clones in 0.63 s; a 142-file / 6.9 MB vault clones in 0.74 s and pushes in 0.87 s. Not one of those numbers is sgit being slow; they are the floor of starting a Python process and deriving a key.
- newsroom, local server: a one-folder clone 0.59 s at 100 files, 0.77 s at 2,000; ten scoped clones at once in 1.9 s.
- The comms vault from local storage (same 35k objects, this sandbox): the same sandbox, the same CLI, the same 35,423 objects, three transports:
| Clone / operation | Hosted API (Lambda + S3 + CloudFront, via proxy) | Static: sgit serve --static (plain GETs, local disk) | Local API: sgit serve --runtime uv (one Python process, disk) | |---|---|---|---| | full clone | 3 min 30 s | 110.6 s (bulk fetch 71.5 s, tree decrypt ~33 s) | 69.1 s (commits 1.4 s, trees 40.7 s, blobs 18.8 s, checkout 2.7 s) | | --depth 1 | 57.4 s | 25.4 s | 11.7 s | | --path security --depth 1 (120 files) | 13.0 s | 2.3 s | 0.8 s | | --path docs, all 1,227 commits | 181 s | | 7.1 s | | --sparse, all history | 206 s | 67.0 s | 43.3 s | | cat of one file on demand | 2.5 s | 1.8 s | 1.8 s | | pull, up to date | 2.5 s | | 0.8 s |
Read down the columns: the per-commit history walk that costs 181 s against the hosted API is 7 s against a local server (the same 1,227 requests at ~1 ms each); a one-folder clone is 0.8 s. What is left when the network is gone is the client's own work, which is decrypting 16,391 trees (about 33 to 40 s on a full clone) and nothing else of note. (Observation for the SG/Send brief: the local disk server answered the store listing with 0 objects, so the full clone there went by the graph walk rather than the bulk sweep; the walk found everything.)
The API does two things that storage cannot: it protects writes (the access token, compare-and-swap on the ref and the branch index) and it shortens reads a little (batches of 50, a listing for cold full clones). Both are small, and both can be done differently on different storage. Everything else is a GET of an immutable object. So "how fast is sgit" is the wrong question; the right one is "where is the storage, and how close": memory, a local disk, an S3 bucket behind a CDN, GitHub Pages, a folder. The deployment table in §5 is that list.
10. sgit is a standard, not a server
The article treats sgit as a product with a server. It is closer to a convention for laying files out, and the proof is that everything in a vault is reachable from one hash with no help from anyone:
- The vault key gives the vault id and, one way, the read key.
- The read key gives the ref's name:
ref-pid-muw-+ the first 12 hex ofHMAC(read_key, "sg-vault-v1:file-id:ref:" + vault_id). Nobody told you; you computed it. - The ref holds the head commit id; the commit holds the tree id; trees hold blob ids and encrypted names. Ref → commit → tree → blob is four GETs for any file, from any host that serves the paths.
- Every object but the ref and the index is named by the hash of its ciphertext, so it is immutable, verifiable and cacheable by whoever holds it. And the name says so: an immutable object's id carries
-imm-(obj-cas-imm-…), the mutable ones carry-muw-(ref-pid-muw-…). Anything in the path, a browser, a proxy, a CDN, a CI cache, a vault app, can decide to keep an object for ever by looking at its name, without reading a byte of it, and without being told by a header. That is how the browser vault app and the sgit.ai pages get their speed: they cache every-imm-object on first sight and refetch only the ref. It is also the trade sgit made on purpose: no deltas between versions of a file (a changed file is a new immutable object), in exchange for objects that any intermediary can cache blindly. Git stores deltas and must be told what changed; sgit stores whole objects and never has to ask. - Writes are the same objects PUT in batches, plus a compare-and-swap on the ref.
That is the whole wire format, and two independent implementations speak it (the CLI; the browser vault app, which vault-embed.js reduces to about 90 lines). A static host implements the read half with nothing installed. The cache layer (sgit cache add <path>, shipped) extends the same idea to hot records: a declared path gets a client-computable cache object under bare/cache/, maintained by push, so a reader with only vault_id + read key does one batch read (the ref plus the candidate ids) and gets the file with no tree walk; a miss or a stale entry falls back to the walk, so correctness never depends on it. The reference reader is written to run in an edge function or a Lambda that serves one hot record. Scion's "derived data" is the same move, computed on a server that can read the plaintext; sgit's is computed by the writer and named by the key.
11. History, blame and merges: where they belong
Correct the article's framing, and then disagree with Scion's. In sgit, history (log, --file, --stat, diff, show, reflog), undo, revert, tags, signature verification and three-way merges all run on the client, and that is the design, not a shortfall. Blame is the one Scion feature sgit does not have as a command today; it is a fold over path history and nothing in the format prevents it. (The honest edge: on this vault history log --file over 1,295 commits took 6 min 8 s (5½ min of CPU), because it flattens every commit's tree; it should skip subtrees whose id did not change, the way the clone walk does.)
The argument for the client: a merge is a workflow step, not a storage operation. It needs the context of the change, the history, and the policy that says who may merge what. In ruv.sgit every worker writes to its own branch and the host merges them per the run's mandate, with a receipt; in the comms vault ten agents meet on a named branch and resolve on the clone that has the conflict in front of it. A server that merges for you must be trusted with the plaintext and with the policy. Scion's own post concedes the point from the other side: it could not keep a merge queue, so "concurrent changes can conflict, but Claude can typically fix these conflicts" — the merge went back to the agent.
And keep what made git git. Scion is in the Piper and Sapling line, a centralised system with one authoritative store, and the post says plainly it "is by no means a suitable replacement for git for general use". sgit keeps the distributed property: a full clone is still one command and a complete, verifiable copy of everything, and anyone with the key can rebuild the whole thing from any host. What changed is that the full clone became optional. --path, --depth, --sparse and the cache layer mean a repository can hold large files without every client paying for them, which is the one habit every git team has had to police by hand. The whole-history clone is a backup and an audit, and it is still there when you want it.
12. Encryption as a constraint
The article sells encryption as privacy. The stronger claim is that it is a design constraint that forbids the shortcut. Because the server cannot read a byte, there is no "just index it on the server", no "just render it there", no server-side merge, no derived data the client did not compute. Every feature had to be expressible as objects the client writes and other clients can derive by key, which is why the results are small: a read is four GETs, a cache is a path-derived object, a deployment is any storage. The newsroom's No server, by design is the same argument applied to a whole site. Say it as a property, not a scolding: the constraint is what produced the simple designs.
13. ruv.sgit, the integration the article does not mention
ruv.sgit.ai runs a Ruflo hive (rUv / ruvnet; a queen and parallel workers) with one ephemeral sgit vault per run and an Agent Behaviour Policy compiled into Ruflo's enforce mode. The ledger (data/vaults.json, 11 runs to 15 Oct) records every sgit operation with its milliseconds, the server-held bytes, a plaintext probe of the server's store (0 hits in every run), a fresh clone checked identical to what was delivered, and the vault deleted at close. It is the agent-swarm case Scion's post is about, run on a local server, and it is where the "storage, not sgit" numbers in §9 come from. Link it as the swarm evidence, next to Agent workflows on ephemeral vaults.
14. What was not done
- Nothing pushed to the comms vault; nothing changed on the site. The site changes in §6 are a list for the site agent.
- The commit-chain object (§3) is a design, not code. It is the one change that would make partial clones of long histories as fast as the depth-1 case; it needs a format note and a migration-free write path (a mutable, encrypted, per-branch object the client rewrites on push).
This page as markdown · its source, admin/content/articles/response-to-scion-and-sgit.md