about things notes.zzstoatzz.io
notes
notes operations tangled.md
8.6 kB

tangled: authority follows the object #

Tangled is a Git collaboration system built across AT Protocol records and specialized servers. The practical mistake is to treat tangled.org as the single authority. It is the browser-facing appview; repository metadata, Git objects, pipeline state, and collaboration records have different owners.

the map #

object authority useful interface
account identity the account's DID document and PDS handle resolution, PLC directory, PDS XRPC
repository metadata sh.tangled.repo in the owner's AT repo Bobbin or the owner's PDS
Git objects and refs the repository's knot Git transport or knot XRPC
current CI execution and logs the repository's selected spindle spindle sh.tangled.ci.* XRPC
issues, pulls, comments, labels AT records, with some appview-derived state Bobbin for indexed reads; PDS for writes
browser auth and settings the Tangled appview tangled.org

Bobbin at https://api.tangled.org is Tangled's public, unauthenticated, read-only index and Git proxy. It is the convenient discovery layer, not a replacement for every object's owner. In particular, a current spindle is the authority for pipeline history since Tangled v1.16.

three identifiers that look interchangeable but are not #

A repository normally involves:

  1. the owner DID — the person's AT identity;
  2. the repo-record AT-URI — at://<owner-did>/sh.tangled.repo/<rkey>;
  3. the repository DID (repoDid) — the stable identity of the Git repository itself.

New repo records usually use the repository name as their rkey and omit value.name. Legacy records use a TID rkey and carry the human name in value.name. Code that resolves repositories must support both.

The DID embedded in a modern Tangled Git remote can be the repository DID, not the owner's DID:

git@tangled.org:did:plc:...

Issue, pull, and pipeline lists are generally keyed by the bare repoDid. Owner-level record discovery is keyed by the owner DID. Do not substitute one for the other because both happen to be PLC DIDs.

lifecycle of a repository and its CI #

  1. Tangled writes an sh.tangled.repo record to the owner's PDS.
  2. That record names the knot holding Git objects and includes repoDid.
  3. A spindle is registered separately under the operator's identity.
  4. The spindle must verify that GET /xrpc/sh.tangled.owner reports the same owner DID.
  5. A repository owner selects the verified spindle in repository pipeline settings. Registration and repository selection are separate operations.
  6. A push, pull request, or manual dispatch matching a file in .tangled/workflows/ creates a pipeline on the selected spindle.

An sh.tangled.spindle record proves registration, not that a particular repository selected it. A verified spindle missing from a repository dropdown usually means the page needs refreshing, verification has not completed, or the viewer is not the repository owner.

Useful browser routes:

  • https://tangled.org/settings/spindles
  • https://tangled.org/<owner>/<repo>/settings?tab=pipelines
  • https://tangled.org/<owner>/<repo>/pipelines

workflow files #

Each YAML file under .tangled/workflows/ is an independent workflow. A pipeline may contain several workflows, which run in parallel; steps within a workflow run serially.

A small check workflow:

when:
  - event: ["push", "manual"]
    branch: ["main"]
  - event: ["pull_request"]
    branch: ["main"]

engine: nixery

dependencies:
  nixpkgs:
    - nodejs_22
    - bun

steps:
  - name: install dependencies
    command: npm ci
  - name: check
    command: npm run check

Choose the engine by workload:

  • nixery builds an environment from Nix packages and runs steps in containers. It is the lighter default for linting, tests, and ordinary builds.
  • microvm runs the whole workflow in a VM. Use it when the workload needs a full machine boundary, KVM-backed execution, services, or Docker inside the guest. Availability and image names depend on the spindle operator.

Pipeline environment values in workflow YAML are public. Repository secrets belong in the repository's pipeline settings and are injected by the spindle.

inspect repositories and collaboration state #

tangled-mcp provides the ergonomic read path:

  • get_repo resolves owner/repo, repo-record AT-URIs, and bare repo DIDs;
  • list_files, read_file, commit_log, and compare read Git data from the knot;
  • issue and pull tools read indexed collaboration records;
  • writes put records on the authenticated user's PDS.

The MCP's Bobbin client deliberately resolves metadata first, then talks to the knot directly for Git data. Bobbin can rate-limit or lag, and a legacy repo record can require the TID-rkey fallback.

Its list_pipelines tool reads Bobbin's sh.tangled.pipeline.listPipelines index. Treat that as discovery, not an authoritative fresh-run oracle. An empty Bobbin result does not prove that the selected spindle has no run.

check current CI without the interactive viewer #

First resolve the repo record and obtain both repoDid and spindle. The record itself is the source of the selected spindle:

curl -fsSG 'https://<pds>/xrpc/com.atproto.repo.getRecord' \
  --data-urlencode 'repo=<owner-did>' \
  --data-urlencode 'collection=sh.tangled.repo' \
  --data-urlencode 'rkey=<repo-name>' |
  jq '{uri, repoDid: .value.repoDid, spindle: .value.spindle, knot: .value.knot}'

For a legacy TID-rkey record, use Bobbin or page com.atproto.repo.listRecords and match value.name.

Then ask the selected spindle directly:

curl -fsSG 'https://<spindle>/xrpc/sh.tangled.ci.queryPipelines' \
  --data-urlencode 'repo=<repo-did>' \
  --data-urlencode 'commits=<full-sha>' \
  --data-urlencode 'limit=5' |
  jq '{total, pipelines: [.pipelines[]? | {id, trigger, workflows}]}'

Each workflow carries status, startedAt, and finishedAt. This is a finite, machine-readable status check and the right source for a recent run.

The spindle owner is independently inspectable:

curl -fsS 'https://<spindle>/xrpc/sh.tangled.owner' | jq .

It must match the DID configured as SPINDLE_SERVER_OWNER for verification to succeed.

the SSH log viewer is a TUI #

A push may print a command like:

ssh -t -p 3333 tangled.org <repo-did> <commit-sha>

This opens an interactive pipeline viewer. It is useful in a real terminal or tmux, but it remains open after the workflow reaches a terminal state and waits for q. Therefore:

  • do not use the viewer as a finite completion watcher;
  • do not put it in a background-job abstraction that wakes only on process exit;
  • use spindle XRPC for machine status;
  • use the SSH viewer when a human wants live logs, then quit it explicitly.

ssh -tt may be necessary through a wrapper with no local terminal, but forced PTY allocation does not make the viewer non-interactive.

troubleshooting by authority #

No workflow appeared

  1. confirm the workflow file existed in the pushed commit;
  2. confirm its event and branch/tag constraints match;
  3. read the repo record's spindle field—do not assume the hosted spindle;
  4. query that spindle with the repository DID;
  5. if the spindle was rebuilt, re-saving the repository's spindle selection may be required so it ingests the repo again.

A self-hosted spindle is not selectable

  1. register it in spindle settings;
  2. verify its public sh.tangled.owner response matches the operator DID;
  3. press retry if verification has not completed;
  4. refresh repository pipeline settings and select it there.

Bobbin and the spindle disagree

This is possible: Bobbin is an indexed AT-record view, while post-v1.16 current pipeline state belongs to the spindle. Use Bobbin for discovery and the selected spindle for the run verdict.

Git reads work but metadata reads fail

The knot and Bobbin are separate layers. Query the knot by repoDid for Git data; inspect Bobbin coverage or rate limiting for indexed records.

sources and local evidence #