chadtmiller.com/knotless
git that lives in your Atmosphere account
The space type private repositories are has a lexicon, dev.knotless.space
MergedThis adds the dev.knotless.space lexicon and teaches harness/lexicons.mjs to accept space lexicons. The harness drops only goat's "unexpected schema type: space" finding, and only for a file whose main is a space. It then checks the file itself: key, name length, collections that are record lexicons, and no collection removed since base. npm run lex removes the file from its copy because @atproto/lex 0.4.4 can't parse it. The docs in access.md and gateway.md are updated to match.
I found nothing that must change.
- The harness logic reads correctly. The relative paths used for
spacesline up with goat'sfile-path, since goat is run from the repo root onlexicons/. - The collection list matches what the description says, and the Rust code doesn't glob
lexicons/, so the new file shouldn't affect it. - Keeping the scopes at
space:*until the lexicon is published is the right order, for the reason the body gives.
Only check has passed so far. The other checks, lexicons, web, docs, test, shots and e2e, were still queued when I looked. The lexicons and web checks are the ones that exercise the harness change and the npm run lex edit, so wait for them to pass before merging.
9 passed, 1 skipped
What changes
A lexicon for the space type private repositories are.
lexicons/dev/knotless/space.jsonisdev.knotless.space, of"type": "space", modelled on grain'ssocial.grain.group: keyany(the repository's name), a name for consent screens, and the collections a private repository's space holds. Those are the ones knotless writes there: the repository's own records (repo,ref,claim,pack,redirect), what members write of it (pull,issue,comment,state,hide,block,queue,label,reaction), a runner'scheck, an owner'srerun, and an organization'sorgMember(the collectionsknotless-serve'sspaces.rssyncs, less the org's ownorgandorgAcceptance, which are public and in the org's own space, pluspackandrerun, which the client writes there but the pages do not read).publish-lexicons.ymlpublishes it on merge: the authorityknotless.devalready has its_lexiconrecord, so no DNS changes (its plan, run here: 1 to put, 0 to delete, 159 unchanged).The checks take space lexicons. goat 0.2.6 refuses the whole file (
schema-json-parse: unexpected schema type: space), as it does grain's.harness/lexicons.mjslets that one finding go, only for a file whosemainis a space, and checks such a file itself against the alpha'slexiconSpaceSchema: its only def ismain, a valid key, a name of 1 to 64 characters, and collections that are record lexicons here; and, against main, that no collection is taken out, since a scope naming the type alone is granted the lexicon's collections.@atproto/lex0.4.4, which makes the pages' types, fails the build on it too (--excludedoes not help, as it parses first), sonpm run lexremoves it from its copy; the pages make no type of a space.@atproto/lexicon0.7.17 inharness/answers.mjstakes it as it is.Docs.
access.md's Scopes andgateway.mdsay why the scopes still name every space type.The scopes do not change here: that is the pull after this, once the lexicon is published.
Why
Both OAuth scopes say
space:*, every space of every type, so a knotless login can read and write other apps' spaces, grain's group pools among them. Namingdev.knotless.spaceneeds its lexicon.This is the first of two pulls rather than one because of what the spaces alpha (11c5d98c, the
testande2epin) does with a named type.OAuthProviderbuilds the authorization page's data withlexiconManager.getSpacesFromScope(scope), which resolves the lexicon of every space type the scope names, with or withoutcollection=, and turns any failure intoinvalid_scope: the login is refused. (Token issuance,buildTokenScope, resolves only a barespace:<type>with no collections, to fill them in from the lexicon.) So a scope namingdev.knotless.spacedeployed before the lexicon is published would break every login on a PDS with spaces. Lexicon first, scopes once it resolves.How it was checked
node harness/lexicons.mjspasses, and fails as it should on a bad key and a collection with no lexicon.harness/answers.mjspasses with@atproto/lexicon0.7.17.lex buildwith 0.4.4 fails with the file and makes all 318 types without it.deploy/publish-lexicons.mjsplans it as one schema to put. The rest is left to CI.Shortcomings
The scopes are still
space:*. Narrowing them, the next pull, has two things to settle:dev.knotless.org, which is also the NSID of its public record, so it can have no space lexicon, and a scope naming it would be refused by the alpha ("Lexicon document is not a space"). Naming onlydev.knotless.spacewould cut members off from writing theirorgAcceptancein the org's space and the gateway from making it. It needs a type of its own, and existing organizations' spaces moved to it.dev.knotless.spacein its PDS's lexicon store before logging in.🤖 Generated with Claude Code