This walkthrough shows how to run a
GitHub Actions
workflow on a
Tangled
repository using the
shared spindle
(
*.gha.spindle.tangled.fedcicd.com
).
A spindle is Tangled's CI runner. The shared spindle reads the standard
.github/workflows/*.ymlfiles in your repo and executes them through its policy engine. You are pushing a GitHub Actions workflow , not a native.tangled/workflowspipeline.
Each user gets their own shared-spindle hostname derived from their DID:
<your-did-with-colons-replaced-by-hyphens>.gha.spindle.tangled.fedcicd.com
For example, the DID
did:plc:lpfuqerea3deuoyrn7ojser4
becomes:
did-plc-lpfuqerea3deuoyrn7ojser4.gha.spindle.tangled.fedcicd.com
Open
Settings → Spindles
(
https://tangled.org/settings/spindles
).
In Register a spindle , enter your shared-spindle hostname and click Register .
The spindle is added to
Your spindles
in the
Unverified
state.
Click
Retry
(verify) next to the spindle. Tangled reaches the spindle's
/xrpc/sh.tangled.owner
endpoint and confirms it is owned by your DID. Once it
succeeds the badge flips to
Verified
.
Go to your repo's
Settings → Pipelines
tab
(
/<owner>/<repo>/settings?tab=pipelines
). In the
Spindle
dropdown, select
your shared-spindle hostname.
Save. Pipelines are now enabled for the repo (a Secrets section appears).
You need two workflow files with the same basename :
.tangled/workflows/<name>.yml
— a native Tangled manifest. Its presence is
what makes the knot's git post-receive hook emit the
sh.tangled.pipeline
event on push.
Without it the run never appears
, even with the spindle
configured.
.github/workflows/<name>.yml
— the actual GitHub Actions workflow the shared
spindle executes.
Clone over SSH and add both:
git clone git@tangled.org:<your-repo-did> repo
cd repo
mkdir -p .github/workflows .tangled/workflows
.github/workflows/ci.yml
(what runs):
# GitHub Actions workflow executed by the Tangled shared spindle.
name: CI
on: [push]
jobs:
find:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: find .
.tangled/workflows/ci.yml
(makes the knot emit the pipeline — same basename
ci
):
when:
- event: ["push", "manual"]
branch: ["main"]
engine: "computeContractGHA"
Commit and push to the default branch:
git add .github/workflows/ci.yml .tangled/workflows/ci.yml
git commit -m "ci: run find . on shared spindle"
git push origin main
On push, the knot's post-receive hook records a
sh.tangled.pipeline
for the
commit (because
.tangled/workflows/ci.yml
exists). The shared spindle
(subscribed to the knot's event stream) sees your repo is authorized for it,
detects
.github/workflows
, and submits the matching GHA workflow to its policy
engine.
Open the repo's
Pipelines
tab (
/<owner>/<repo>/pipelines
). The run for
your push appears and moves through
pending
→
running
→
succeeded
.
Open the run to read its console output. The
checkout
group shows the commit
checked out from the knot, and the
find .
step lists the workspace tree:
##[group]checkout
Checked out <sha> from https://knot1.tangled.sh
##[endgroup]
+ find .
.
./.github
./.github/workflows
./.github/workflows/ci.yml
./README.md
...
The shared spindle (
tangled-spindle-minimal
) exposes:
| Route | Purpose |
|---|---|
GET /xrpc/sh.tangled.owner
|
Owner DID — used during Verify |
GET /events
|
Pipeline status WebSocket |
GET /logs/:knot/:pipelineRkey/:workflow
|
Console output |
GET /status/:knot/:pipelineRkey/:workflow
|
Run status JSON |
Flow on each push:
.tangled/workflows/<name>.yml
and creates a
sh.tangled.pipeline
record.
This is the only path that produces the
event
— no
.tangled
manifest, no pipeline.
.github/workflows/<name>.yml
at the pushed ref — the
.tangled
file's job is only to trigger; the
GHA file is what executes
.
pending
→
running
→ terminal) over
/events
.
The two files must share a basename (e.g. both
ci.yml):.tangled/workflowstriggers the run,.github/workflowsdefines what runs.
deno --watch
reacting to edits) the knot event can be
missed with no replay — re-push once it's settled.