Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/install.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Installer controls
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
installer:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Validate standalone entrypoint and refusal controls
run: |
sh -n install.sh
python3 scripts/test-install-release.py
171 changes: 33 additions & 138 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,44 @@
<img src="assets/logo.svg" width="220" alt="Shuttle logo — a weaving shuttle carrying a red weft thread through vertical warp threads, with a small knot seal below"/>
</p>

<h1 align="center">shuttle</h1>
# Shuttle

<p align="center">
<em>🧵 The workflow engine of the quipu stack — every transition signed, every window frozen</em>
</p>
Signed agent workflows with append-only execution records. Steps link back
to work items and export their evidence to Quipu.

<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache-2.0"/></a>
<a href="https://github.com/scbrown/quipu"><img src="https://img.shields.io/badge/stack-quipu-8B5E3C.svg" alt="Part of the quipu stack"/></a>
</p>
## Install

```sh
curl -fsSL https://raw.githubusercontent.com/scbrown/shuttle/main/install.sh | sh
```

Python 3.11+ with venv/pip, curl, and a Linux or macOS shell. Downloads the latest checksummed wheel into its own virtual environment;
command links go in `~/.local/bin`. Add that directory to `PATH`.

Set `STACK_INSTALL_ROOT` on the receiving shell for another prefix.
Pinned releases, alternatives, configuration and removal are in the
[installation and usage reference](docs/installation-reference.md).

> *A shuttle carries the weft through the warp. Runs move through their
> workflow's declared steps; each pass leaves a signed knot in the record.*
## Try it

Workflow engines usually mean a daemon, a queue, a server, and a dashboard
that disagrees with reality. Shuttle is none of those. A **local JSONL log is
the hot path**; [quipu](https://github.com/scbrown/quipu) is the governed
record. Runs move through their workflow's declared steps, every transition is
**signed by the agent that performed it** (per-agent ed25519), and the
append-only history is exported into quipu's time-windowed operational graphs —
where completed windows are **deep-frozen** whole into read-only archives that
stay queryable forever.
Check the installed workflow engine:

Consumers — [shantytown](https://github.com/scbrown/shantytown) crews,
[creel](https://github.com/scbrown/creel) tabs,
[NeuralAmplifier](https://github.com/scbrown/NeuralAmplifier) — read the
graph, never shuttle.
```sh
shuttle version
```

[Create a workflow](docs/installation-reference.md#quick-start) when you are ready
to register an agent identity. Installation does not enroll a key or run a workflow.

## Next steps

- [Tambo](https://github.com/intigraph/tambo): guided stack installation and verification.
- [Camayoc](https://github.com/scbrown/camayoc): governed memory and vocabulary.
- [Detailed examples and agent setup](docs/installation-reference.md).

## Stack overview
**Load this repository’s graph:** public repository-pendant assets have not yet been verified. When a release supplies the archive, checksum and provenance, follow the [verified import workflow](https://scbrown.github.io/quipu/sharing/index.html): extract the share directory and use `quipu import SHARE_DIR --db repository.db`. Import stages knowledge; ROOT stays unchanged.

## The stack

<!-- stack-table:start -->

Expand All @@ -56,118 +65,4 @@ when.

<!-- stack-table:end -->

## Role in the stack

```text
agents / crews (advance runs; each step signed)
│
▼
shuttle (state machine + JSONL outbox)
│ export: signed Turtle, idempotent batches
▼
quipu (windowed graphs · deep-frozen archives)
│
▼
shantytown · creel · NeuralAmplifier (read the graph, never shuttle)
```

- **[camayoc](https://github.com/scbrown/camayoc)** owns the vocabulary:
the workflow slice (`WorkflowRun`, `TransitionEvent`, …), each term owed to a
competency question in `competency/workflow-and-archive.md`.
- **[caboodle](https://github.com/scbrown/caboodle)** installs and verifies
shuttle as part of the box.

## Quick start

```bash
pip install -e . # Python 3.11+, one dep (cryptography)

shuttle keys init agent-a --workflow triage
# -> prints the public key + registration Turtle; a HUMAN loads it into
# the dataKind=identity graph. Shuttle never registers its own keys.

shuttle define examples/triage.json
shuttle start triage run-1 --agent agent-a
shuttle advance run-1 --step claim --agent agent-a # signed
shuttle advance run-1 --step work --agent agent-a
shuttle advance run-1 --step finish --agent agent-a # terminal
shuttle status

shuttle export # drain the log into quipu windows
shuttle verify run-1 # re-verify signatures FROM the graph
shuttle freeze-window 2026-07 # archive a completed window
```

## The shape

| file | owns |
|---|---|
| `shuttle/model.py` | the pure state machine; events are the truth, state is a fold |
| `shuttle/state.py` | the append-only JSONL outbox + export watermark |
| `shuttle/signing.py` | per-agent ed25519 (0600 host files), `shuttle-transition-v1` canonical message |
| `shuttle/quipu_client.py` | windowed writes ride `/knot`'s STRICT `graph` lane; a SHACL refusal is a refusal, never a silent 200 |
| `shuttle/windows.py` | `{ns}/window/shuttle/runs/{YYYY-MM}` graphs + the `urn:shuttle:dataset:open` dataset |
| `shuttle/export.py` | log → signed Turtle → idempotent `/episode` batches |
| `shuttle/seeds.py` | seed steps: a [seeds](https://github.com/scbrown/seeds) work item performs the step, `reconcile` advances the run when it closes |
| `shuttle/cli.py` | the whole v1 surface |

Design: [docs/design/shuttle.md](docs/design/shuttle.md).

## Seed steps

A transition can be performed by a [seeds](https://github.com/scbrown/seeds)
work item instead of by hand:

```json
{"step": "review", "from": "open", "to": "reviewed",
"seed": {"title": "Review the change", "labels": ["review"]},
"on_abandon": "reject"}
```

`shuttle reconcile --agent <you>` (on a timer, or by hand) does two things for
every open run:

- For each seed step legal from the run's state, it runs `sd create
--workflow-run <run> --step <step> --visit <n>`. seeds derives the id from
that key, so repeating the create returns the same seed. A run that re-enters
the state gets a new visit and a new seed.
- It reads the seed with `sd show --json`. Outcome `done` advances the run
through the step, signed, with the seed id, outcome and close time recorded
as evidence. Any other outcome takes `on_abandon`. With no `on_abandon` the
run is FLAGGED (exit 1) and never advanced silently.

seeds knows nothing about workflows beyond the link field. A missed tick costs
one tick, not a stuck run. `SHUTTLE_SD` names the `sd` binary; where seeds
stores its work is sd's own configuration.

## What runs today

v1 is landed and in production use: signed runs, windowed export, freezable
history, seed steps, and the cross-repo acceptance passing against a live
quipu. On the fleet host it carries the ansible apply lane, repo watch and
stack watch workflows, and an unattended actuator installs each release.

```bash
just check # parse + file-size ratchet
just test # check + the unit suite
just e2e # the cross-repo acceptance against a live quipu
```

Documentation: the [shuttle book](https://scbrown.github.io/shuttle/).
Changes: [CHANGELOG.md](CHANGELOG.md).

## License

[Apache-2.0](LICENSE). Releases up to and including v0.5.0 were published under the MIT License and remain available under it.

For pre-registered job identities, put `--require-existing-keys` before the
subcommand (for example, `shuttle --require-existing-keys advance ...`). It
requires an existing Ed25519 key owned by the current Unix user, in a regular
0600 file; missing keys, final-component symlinks and unsafe files are refused.
The policy covers the whole command, including export verification, and never
creates a replacement key. Older installations reject this flag. Default
first-use key initialization remains available for the normal registration
ceremony. This option checks local custody, not remote registration or job
scope admission. SDK callers can use `signing.existing_keys_only()` around an
entire operation, or `sign_transition(..., require_existing_key=True)` for one
signature. Parent directory custody remains the operator's responsibility.
[License](LICENSE).
149 changes: 149 additions & 0 deletions docs/installation-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<p align="center">
<img src="assets/logo.svg" width="220" alt="Shuttle logo — a weaving shuttle carrying a red weft thread through vertical warp threads, with a small knot seal below"/>
</p>

<h1 align="center">shuttle</h1>

<p align="center">
<em>🧵 The workflow engine of the quipu stack — every transition signed, every window frozen</em>
</p>

<p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License: Apache-2.0"/></a>
<a href="https://github.com/scbrown/quipu"><img src="https://img.shields.io/badge/stack-quipu-8B5E3C.svg" alt="Part of the quipu stack"/></a>
</p>

> *A shuttle carries the weft through the warp. Runs move through their
> workflow's declared steps; each pass leaves a signed knot in the record.*

Workflow engines usually mean a daemon, a queue, a server, and a dashboard
that disagrees with reality. Shuttle is none of those. A **local JSONL log is
the hot path**; [quipu](https://github.com/scbrown/quipu) is the governed
record. Runs move through their workflow's declared steps, every transition is
**signed by the agent that performed it** (per-agent ed25519), and the
append-only history is exported into quipu's time-windowed operational graphs —
where completed windows are **deep-frozen** whole into read-only archives that
stay queryable forever.

Consumers — [shantytown](https://github.com/scbrown/shantytown) crews,
[creel](https://github.com/scbrown/creel) tabs,
[NeuralAmplifier](https://github.com/scbrown/NeuralAmplifier) — read the
graph, never shuttle.

## Stack overview

## Role in the stack

```text
agents / crews (advance runs; each step signed)
│
▼
shuttle (state machine + JSONL outbox)
│ export: signed Turtle, idempotent batches
▼
quipu (windowed graphs · deep-frozen archives)
│
▼
shantytown · creel · NeuralAmplifier (read the graph, never shuttle)
```

- **[camayoc](https://github.com/scbrown/camayoc)** owns the vocabulary:
the workflow slice (`WorkflowRun`, `TransitionEvent`, …), each term owed to a
competency question in `competency/workflow-and-archive.md`.
- **[caboodle](https://github.com/scbrown/caboodle)** installs and verifies
shuttle as part of the box.

## Quick start

```bash
pip install -e . # Python 3.11+, one dep (cryptography)

shuttle keys init agent-a --workflow triage
# -> prints the public key + registration Turtle; a HUMAN loads it into
# the dataKind=identity graph. Shuttle never registers its own keys.

shuttle define examples/triage.json
shuttle start triage run-1 --agent agent-a
shuttle advance run-1 --step claim --agent agent-a # signed
shuttle advance run-1 --step work --agent agent-a
shuttle advance run-1 --step finish --agent agent-a # terminal
shuttle status

shuttle export # drain the log into quipu windows
shuttle verify run-1 # re-verify signatures FROM the graph
shuttle freeze-window 2026-07 # archive a completed window
```

## The shape

| file | owns |
|---|---|
| `shuttle/model.py` | the pure state machine; events are the truth, state is a fold |
| `shuttle/state.py` | the append-only JSONL outbox + export watermark |
| `shuttle/signing.py` | per-agent ed25519 (0600 host files), `shuttle-transition-v1` canonical message |
| `shuttle/quipu_client.py` | windowed writes ride `/knot`'s STRICT `graph` lane; a SHACL refusal is a refusal, never a silent 200 |
| `shuttle/windows.py` | `{ns}/window/shuttle/runs/{YYYY-MM}` graphs + the `urn:shuttle:dataset:open` dataset |
| `shuttle/export.py` | log → signed Turtle → idempotent `/episode` batches |
| `shuttle/seeds.py` | seed steps: a [seeds](https://github.com/scbrown/seeds) work item performs the step, `reconcile` advances the run when it closes |
| `shuttle/cli.py` | the whole v1 surface |

Design: [docs/design/shuttle.md](https://github.com/scbrown/shuttle/blob/main/docs/design/shuttle.md).

## Seed steps

A transition can be performed by a [seeds](https://github.com/scbrown/seeds)
work item instead of by hand:

```json
{"step": "review", "from": "open", "to": "reviewed",
"seed": {"title": "Review the change", "labels": ["review"]},
"on_abandon": "reject"}
```

`shuttle reconcile --agent <you>` (on a timer, or by hand) does two things for
every open run:

- For each seed step legal from the run's state, it runs `sd create
--workflow-run <run> --step <step> --visit <n>`. seeds derives the id from
that key, so repeating the create returns the same seed. A run that re-enters
the state gets a new visit and a new seed.
- It reads the seed with `sd show --json`. Outcome `done` advances the run
through the step, signed, with the seed id, outcome and close time recorded
as evidence. Any other outcome takes `on_abandon`. With no `on_abandon` the
run is FLAGGED (exit 1) and never advanced silently.

seeds knows nothing about workflows beyond the link field. A missed tick costs
one tick, not a stuck run. `SHUTTLE_SD` names the `sd` binary; where seeds
stores its work is sd's own configuration.

## What runs today

v1 is landed and in production use: signed runs, windowed export, freezable
history, seed steps, and the cross-repo acceptance passing against a live
quipu. On the fleet host it carries the ansible apply lane, repo watch and
stack watch workflows, and an unattended actuator installs each release.

```bash
just check # parse + file-size ratchet
just test # check + the unit suite
just e2e # the cross-repo acceptance against a live quipu
```

Documentation: the [shuttle book](https://scbrown.github.io/shuttle/).
Changes: [CHANGELOG.md](https://github.com/scbrown/shuttle/blob/main/CHANGELOG.md).

## License

[Apache-2.0](https://github.com/scbrown/shuttle/blob/main/LICENSE). Releases up to and including v0.5.0 were published under the MIT License and remain available under it.

For pre-registered job identities, put `--require-existing-keys` before the
subcommand (for example, `shuttle --require-existing-keys advance ...`). It
requires an existing Ed25519 key owned by the current Unix user, in a regular
0600 file; missing keys, final-component symlinks and unsafe files are refused.
The policy covers the whole command, including export verification, and never
creates a replacement key. Older installations reject this flag. Default
first-use key initialization remains available for the normal registration
ceremony. This option checks local custody, not remote registration or job
scope admission. SDK callers can use `signing.existing_keys_only()` around an
entire operation, or `sign_transition(..., require_existing_key=True)` for one
signature. Parent directory custody remains the operator's responsibility.
10 changes: 5 additions & 5 deletions docs/knowledge/repository-knowledge.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"source_revision": "33001f173986a0496b3fc9f0afe4ebc658e2618f",
"reviewed_at": "2026-10-21T23:38:05.033622Z",
"source_revision": "d9c5a8646b5063bfdeb95401a8ff69f2dad78242",
"reviewed_at": "2026-10-24T03:35:26.188814Z",
"max_age_seconds": 2592000,
"records": [
{
"kind": "idea",
"source": "README.md",
"subject": "http://aegis.gastown.local/ontology/doc/shuttle/README.md",
"summary": "Shuttle records workflow transitions in a local append-only JSONL log and exports signed history into governed, time-windowed Quipu graphs.",
"source_sha256": "fa05782b45f3fe77b56d59c22c57de6409fcda5cf3bc6008058b2df1d2d674c3",
"summary": "Shuttle records signed agent workflow transitions in append-only records and exports their evidence to Quipu.",
"source_sha256": "964ce6b302291a32c23a9feee377dabfd778d3b7e4d79cfe2e06f368f02843de",
"type": "Concept"
},
{
Expand Down Expand Up @@ -100,7 +100,7 @@
"source": "README.md",
"subject": "http://aegis.gastown.local/ontology/doc/shuttle/README.md",
"summary": "Begin with the workflow quick start and role in the stack; registration and production writes remain explicit operator actions.",
"source_sha256": "fa05782b45f3fe77b56d59c22c57de6409fcda5cf3bc6008058b2df1d2d674c3",
"source_sha256": "964ce6b302291a32c23a9feee377dabfd778d3b7e4d79cfe2e06f368f02843de",
"properties": {
"startHereOrder": 1,
"glossaryTerm": "https://scbrown.github.io/quechua/ns#Decision"
Expand Down
Loading
Loading