Identifiers and Git URLs
Tangled names accounts, repositories and records with AT Protocol identifiers, and names commits and branches with ordinary Git identifiers. This page lists each form, what it points at, and when it changes.
git clone git@knot.example.com:alice.example.com/projgit clone https://knot.example.com/did:plc:4kx2vqmn7d3rjzy5lbk6hq3c| Identifier | Example | Names | Changes when |
|---|---|---|---|
| Handle | alice.example.com | An account | The owner changes it |
| Account DID | did:plc:… | A user or organization | Never |
| Repository DID | did:plc:… | One repository | Never, including renames and transfers |
| Service DID | did:web:knot.example.com | A knot, spindle or other service | Its hostname changes |
| AT URI | at://did:plc:…/org.tangled.track.ticket/3l… | One record | The record is deleted |
| CID | bafyrei… | One version of a record | The record changes |
| NSID | org.tangled.track.ticket | A record type or XRPC method | Never |
| Git object ID | a1b2… (40 or 64 hex) | One commit, tree or blob | Its content changes |
| Git ref | refs/heads/main | A branch or tag | It moves on push |
| Pipeline ID | spindle-local | One CI run on one spindle | Never; meaningless on another spindle |
Handles and DIDs
Section titled “Handles and DIDs”A handle is the readable name of an account and resolves to a DID.
Records and permissions store the DID, so changing a handle does not
rewrite anything. Web URLs accept either form:
https://tangled.org/alice.example.com/proj and
https://tangled.org/did:plc:…/proj show the same repository.
Users and repositories use did:plc. Knots and spindles use did:web
derived from their hostname: a knot at knot.example.com is
did:web:knot.example.com, and its DID document is served at
https://knot.example.com/.well-known/did.json.
Repository DIDs
Section titled “Repository DIDs”Every repository has its own did:plc, separate from its owner’s. The
knot that hosts it mints it (or adopts one the client prepared with
org.tangled.repo.reserveKey) and holds its rotation and signing key.
Its DID document lists the knot as both #atproto_pds and
#tangled_knot.
The repository DID is the stable handle for everything about the repository: its records, CI pipelines, secrets, webhooks and git mirror all key on it. The human name is claimed by the owner, under a slug that is the lowercased name:
- The repository account holds
org.tangled.repo.manifest/selfwithdeclaration: {owner, name}. - The owner’s PDS holds
org.tangled.repo.declaration/<slug>withrepo: <repoDid>.
A repository is only visible under owner/slug while both sides agree.
Renaming changes the name, the slug and the declaration; the repository
DID stays the same.
AT URIs and record keys
Section titled “AT URIs and record keys”at://<account DID>/<collection NSID>/<record key>The first segment is the account whose repository holds the record, which is not always the author. Issues, pull requests, comments, reactions, labels and patches live in the repository DID’s account on the knot, so their URIs start with the repository DID:
at://did:plc:repoxyz/org.tangled.track.ticket/3lzq4w5qk2c2aThe person who wrote such a record is in its x-tngl-editor field,
stamped by the knot. Stars, follows, vouches, profiles and strings live
in the author’s own PDS and start with the author’s DID.
Record key (rkey) rules depend on the collection:
| Rule | Collections |
|---|---|
self | actor.profile, repo.manifest, git.refs |
| Repository slug | repo.declaration |
| Subject DID | graph.follow, graph.vouch, graph.collaborator |
| The record’s own CID | review.patch |
| TID, minted by the knot | track.ticket, feed.comment, feed.reaction written through the knot |
| TID | everything else |
Web URLs for issues and pull requests use the ticket’s rkey:
/alice.example.com/proj/issues/3lzq4w5qk2c2a. There are no sequential
numbers.
Records lists every collection.
Git object format
Section titled “Git object format”New repositories on a knot use SHA-256 by default, so object IDs are 64 hex characters. Repositories migrated from the Go knot keep SHA-1 (40 characters). A SHA-256 repository can’t push to or fetch from a SHA-1 one. Don’t assume a fixed hash length in tools.
Clone URLs
Section titled “Clone URLs”A knot serves each repository under two addresses: owner plus name, or the repository DID. The owner can be a handle or a DID.
Over SSH:
git clone git@knot.example.com:alice.example.com/projgit clone git@knot.example.com:did:plc:alice/projgit clone git@knot.example.com:did:plc:repoxyzOver HTTPS:
git clone https://knot.example.com/alice.example.com/projgit clone https://knot.example.com/did:plc:alice/projgit clone https://knot.example.com/did:plc:repoxyz- A trailing
.gitis accepted on the name form (proj.git) but not on the DID form:did:plc:repoxyz.gitwon’t resolve. - The knot’s SSH listener defaults to port 2222. If the knot doesn’t
listen on 22, use
ssh://git@knot.example.com:2222/did:plc:repoxyzor aHostblock in~/.ssh/config. - The DID form keeps working after a rename; the name form follows the current slug.
SSH push authenticates with a key published in your PDS. If your agent
offers several keys and the knot rejects them, add IdentitiesOnly yes
for that host. HTTPS push takes a service-auth token bound to
org.tangled.git.receivePack as the Bearer token or Basic password, supplied
by a credential helper.
Reserved refs
Section titled “Reserved refs”Knots don’t allow pushes to these namespaces. All of these are also not under ref advertisement, other than refs/cobs/* which can be
fetched with an explicit refs/cobs/ ref-prefix, read only.
| Ref prefix | Holds |
|---|---|
refs/cobs/* | Collaborative objects: signed change graphs for collaborators, tickets, comments and more. Append-only. |
refs/cob-checkpoints/* | Knot-written COB snapshots. |
refs/hidden/* | Nothing. Legacy fork staging refs are deleted on boot; the prefix stays reserved. |
refs/atproto/* | The repository DID’s atproto commit chain, PLC queue and tombstones. |
Push options
Section titled “Push options”Push options pass through to the knot’s org.tangled.git.pushEvent (at
most 10) and to spindle.
git push -o skip-ci origin main| Option | Effect |
|---|---|
skip-ci or ci-skip | Spindle does not start pipelines for this push. Push webhooks still fire. |
Workflow Reference describes how pushes trigger pipelines.