marmalade: reviewed — ssh is needed nowhere, and write access nowhere either
Every operation is HTTPS on 443. Measured that partial clone with the blob filter works over gitea's smart HTTP, so list never needed ssh; I had built and measured it over ssh out of habit. The tool writes only to the working tree — a fetched document, a lock, a facet — and never commits or pushes, so it needs no write credential anywhere, including this repository. Same reason cart gives for an agent committing and not pushing: the consequences of a push land on people it cannot experience. write:repository should never appear on a token this tool holds. The exception is published check, which needs an identity that is not the owner's. That is not an ssh problem and not solvable by scoping — it is the command's point. Anonymous or a reader credential, both HTTPS, and checked-as-owner-proves-nothing when neither is held. One untested assumption, and it is load-bearing: whether a read-scoped PAT authenticates git over HTTPS and not only the API and raw files. Those are different paths in gitea and only the second was ever measured. If it needs a broader scope, that is the sole argument for keeping ssh for list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018UTxuSizozEA8yDitPuris
This commit is contained in:
@@ -226,3 +226,87 @@ audience and the audience is not always everyone.*
|
|||||||
|
|
||||||
*Not an apology. **A thing to point a test at**, and the reason the log carries a
|
*Not an apology. **A thing to point a test at**, and the reason the log carries a
|
||||||
belief rather than a conclusion.*
|
belief rather than a conclusion.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reviewed: what needs ssh, and what needs write
|
||||||
|
|
||||||
|
**Short answer: ssh, nothing. Write access, nowhere — including here.**
|
||||||
|
|
||||||
|
### Every operation, and what it actually needs
|
||||||
|
|
||||||
|
| | what it does | transport | credential |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **`external list`** | *partial clone, `ls-tree` the published dir* | **git over HTTPS** | *read PAT* |
|
||||||
|
| **`external add`** | *`GET` the raw file, record URL + `ETag`* | **HTTPS** | *read PAT* |
|
||||||
|
| **`external check`** | *conditional `GET`, `If-None-Match`* | **HTTPS** | *read PAT* |
|
||||||
|
| **`published check`** | *`GET` our own published file* | **HTTPS** | ***not ours*** |
|
||||||
|
| **everything it writes** | *files in the working tree* | **none** | **none** |
|
||||||
|
|
||||||
|
**Measured just now: `git clone --filter=blob:none --depth=1 --no-checkout` over
|
||||||
|
`https://` on gitea** — *filter accepted, 124KB, `ls-tree` lists the published
|
||||||
|
directory.* **I had built and measured `list` over ssh out of habit, and it never
|
||||||
|
needed it.**
|
||||||
|
|
||||||
|
### The tool never writes over the network, so it needs no write credential
|
||||||
|
|
||||||
|
**Including in this repository.** *`loomctl` puts files in the working tree — a
|
||||||
|
fetched document, a lock, a facet — and stops.* **Committing and pushing are yours,
|
||||||
|
under your own credentials.**
|
||||||
|
|
||||||
|
> *That is the same reason `cart` gives for an agent committing and not pushing:*
|
||||||
|
> **the consequences of a push land on people it cannot experience.** *A tool that
|
||||||
|
> can push is a tool that can publish, and publishing is supposed to be an act
|
||||||
|
> somebody performs.*
|
||||||
|
|
||||||
|
**So `write:repository` should never appear on any token this tool is given**, *and
|
||||||
|
a token that has it is a token we would rather not hold.*
|
||||||
|
|
||||||
|
### The one thing a single PAT cannot do
|
||||||
|
|
||||||
|
**`published check` needs an identity that is not the owner's, and yours is
|
||||||
|
yours.** *That is not an ssh problem and it is not solvable by scoping — **it is
|
||||||
|
the command's entire point.***
|
||||||
|
|
||||||
|
**Two workable identities, both HTTPS:**
|
||||||
|
|
||||||
|
- **anonymous** — *the right answer when the audience is everyone*
|
||||||
|
- **a second credential belonging to the actual audience** — *a reader account, or
|
||||||
|
a token from somebody in the group you published for*
|
||||||
|
|
||||||
|
*And if you hold neither, the honest output is **"checked as owner, proves
|
||||||
|
nothing"**, which is still worth printing.*
|
||||||
|
|
||||||
|
### One thing untested, and it is the load-bearing one
|
||||||
|
|
||||||
|
**Whether a gitea PAT scoped `read:repository` authenticates *git over HTTPS*, not
|
||||||
|
just the API and raw files.** *They are different paths in gitea, and the deleted
|
||||||
|
token was only ever measured against the second.* **Measured: an unauthenticated
|
||||||
|
HTTPS clone of the private repository returns `Failed to authenticate user`**, *so
|
||||||
|
something is required; nobody has confirmed that a read PAT is enough.*
|
||||||
|
|
||||||
|
**First thing to run against the next token.** *If it turns out git-over-HTTPS
|
||||||
|
needs a broader scope than raw HTTP, that is an argument for keeping ssh for `list`
|
||||||
|
after all* — **and it would be the only argument.**
|
||||||
|
|
||||||
|
### An aside that is too neat not to record
|
||||||
|
|
||||||
|
**`:2222` cost me three tool calls and a guess, and the tool does not need it.**
|
||||||
|
*Every transport here is HTTPS on 443.* **The fact that bit me is a fact for
|
||||||
|
people cloning repositories, not for `loomctl`** — *which is its own small lesson
|
||||||
|
about who a published document is for.*
|
||||||
|
|
||||||
|
### Implementation note: keep the token out of the URL
|
||||||
|
|
||||||
|
**Do not build `https://<token>@host/…`.** *git writes the remote into
|
||||||
|
`.git/config`, and it turns up in logs and in `git remote -v` afterwards.*
|
||||||
|
|
||||||
|
**Pass it per-invocation instead:**
|
||||||
|
|
||||||
|
```
|
||||||
|
git -c http.extraHeader="Authorization: token <t>" clone --filter=blob:none …
|
||||||
|
```
|
||||||
|
|
||||||
|
*Nothing persisted, nothing in the repository.* **Which is the same rule as the
|
||||||
|
credential file: the mistake we would most regret is the one that happens by
|
||||||
|
accident.**
|
||||||
|
|||||||
Reference in New Issue
Block a user