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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Relay hashes raw bytes and checks generated files byte-for-byte across platforms.
* text=auto eol=lf
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ concurrency:

jobs:
verify:
runs-on: ubuntu-latest
name: Verify on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- name: Check out source
uses: actions/checkout@v6
Expand Down
30 changes: 23 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Project Relay is an evidence-governed coordination layer for multiple AI systems and human reviewers. It uses a Git repository as a shared, inspectable record while MCP provides a provider-neutral integration surface.

> **Status:** pre-alpha public scaffold. The name is a working title, the protocol is unstable, and no production deployment is implied.
> **Status:** pre-alpha M1 candidate. The name is a working title, the protocol is unstable, and no production deployment is implied.

## Why it exists

Expand Down Expand Up @@ -38,14 +38,16 @@ The canonical record is validated JSON plus referenced artifacts in Git history.

## Current public milestone

This repository starts with:
The local M1 vertical slice now includes:

- JSON Schemas for tasks, events, evidence, reviews, and decisions;
- deterministic canonicalisation and SHA-256 provenance helpers;
- an append-only event-chain verifier;
- a read-only MCP server for inspection, validation, and event preparation;
- a static console suitable for a future GitHub Pages deployment;
- CI and a public-boundary scanner.
- append-only event-chain verification plus policy-checked task transitions;
- an evidence-bundle CLI that hashes declared artifacts without executing commands;
- a read-only MCP server with tools, a task resource, and an independent-review prompt;
- a deterministic static console projection of state, history, and gate results;
- a synthetic submission, review, remediation, resubmission, and human-decision fixture;
- Windows and Linux CI conformance coverage plus a public-boundary scanner.

GitHub write automation, remote MCP transport, authentication, and hosted multi-tenant operation are deliberately deferred until their threat models are reviewed.

Expand All @@ -65,6 +67,12 @@ Check direct dependency versions without changing the repository:
npm run versions:check
```

Run the offline local doctor for measured runtime, Git, lockfile, workspace, and worktree checks:

```bash
npm run doctor
```

To review conservative updates and receive an interactive `[y/N]` installation prompt:

```bash
Expand All @@ -87,7 +95,15 @@ The MCP process uses stdio and reads the workspace specified by `RELAY_WORKSPACE
}
```

Use `examples/minimal` as the first workspace.
Use `examples/m1` to inspect the complete local remediation loop. `examples/minimal` remains the smallest M0 kernel fixture.

Create a validated evidence bundle from an explicit manifest:

```bash
npm run evidence:create -- --manifest path/to/manifest.json --output path/to/bundle.json
```

The command reads and hashes declared artifacts. It does not execute the commands recorded in the manifest.

## Safety and publication boundary

Expand Down
2 changes: 1 addition & 1 deletion apps/console/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Relay console

The console is a credential-free, read-only projection of a generated Relay state snapshot. It is intentionally compatible with static hosting such as GitHub Pages.
The console is a credential-free, read-only projection of a generated Relay state snapshot. It shows derived task state, transition history counts, and default-policy gate results. It is intentionally compatible with static hosting such as GitHub Pages.

Run it locally from the repository root:

Expand Down
19 changes: 17 additions & 2 deletions apps/console/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,31 @@ function renderTask(task) {
const strong = document.createElement("strong");
strong.textContent = task.title;
const small = document.createElement("small");
small.textContent = task.id;
small.textContent = `${task.id} · ${task.history.length} transitions · policy ${task.policy_valid ? "valid" : "failed"}`;
title.append(strong, small);

row.append(
title,
cell("badge badge-state", task.state.replaceAll("_", " ")),
cell(
`badge badge-state ${task.policy_valid ? "" : "badge-policy-failed"}`,
task.derived_state.replaceAll("_", " ")
),
cell(`badge badge-risk-${task.risk}`, task.risk),
cell("", String(task.evidence_count)),
cell("", String(task.review_count))
);

const gates = document.createElement("div");
gates.className = "task-gates";
for (const [name, satisfied] of Object.entries(task.gates)) {
gates.append(
cell(
`gate ${satisfied ? "gate-pass" : "gate-fail"}`,
`${satisfied ? "✓" : "×"} ${name.replaceAll("_", " ")}`
)
);
}
row.append(gates);
return row;
}

Expand Down
117 changes: 105 additions & 12 deletions apps/console/state/index.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,122 @@
{
"protocol_version": "0.1.0",
"generated_from": "examples/minimal",
"generated_from": "examples/m1",
"summary": {
"tasks": 1,
"active": 1,
"evidence_bundles": 0,
"reviews": 0,
"decisions": 0
"active": 0,
"evidence_bundles": 2,
"reviews": 2,
"decisions": 1,
"failed_gates": 0
},
"tasks": [
{
"id": "RELAY-0001",
"title": "Validate the Relay protocol kernel",
"question": "Does the initial Relay protocol reject malformed records and detect a modified event chain?",
"state": "draft",
"id": "RELAY-1001",
"title": "Exercise the default remediation policy",
"question": "Can Relay preserve a failed independent review, require remediation, and record human-authorised acceptance?",
"state": "accepted",
"derived_state": "accepted",
"policy_valid": true,
"gates": {
"evidence_present": true,
"independent_review": true,
"independent_reproduction": true,
"remediation_recorded": true,
"human_decision": true,
"final_state_consistent": true
},
"history": [
{
"sequence": 1,
"event_id": "EVT-m1event0001",
"type": "task.created",
"from_state": null,
"to_state": "draft",
"occurred_at": "2026-07-19T01:00:00Z"
},
{
"sequence": 2,
"event_id": "EVT-m1event0002",
"type": "task.ready",
"from_state": "draft",
"to_state": "ready",
"occurred_at": "2026-07-19T01:01:00Z"
},
{
"sequence": 3,
"event_id": "EVT-m1event0003",
"type": "task.claimed",
"from_state": "ready",
"to_state": "claimed",
"occurred_at": "2026-07-19T01:02:00Z"
},
{
"sequence": 4,
"event_id": "EVT-m1event0004",
"type": "task.started",
"from_state": "claimed",
"to_state": "in_progress",
"occurred_at": "2026-07-19T01:03:00Z"
},
{
"sequence": 5,
"event_id": "EVT-m1event0005",
"type": "task.submitted",
"from_state": "in_progress",
"to_state": "submitted",
"occurred_at": "2026-07-19T01:05:00Z"
},
{
"sequence": 6,
"event_id": "EVT-m1event0006",
"type": "review.requested",
"from_state": "submitted",
"to_state": "under_review",
"occurred_at": "2026-07-19T01:06:00Z"
},
{
"sequence": 8,
"event_id": "EVT-m1event0008",
"type": "remediation.requested",
"from_state": "under_review",
"to_state": "remediation",
"occurred_at": "2026-07-19T01:08:00Z"
},
{
"sequence": 9,
"event_id": "EVT-m1event0009",
"type": "task.submitted",
"from_state": "remediation",
"to_state": "submitted",
"occurred_at": "2026-07-19T01:10:00Z"
},
{
"sequence": 10,
"event_id": "EVT-m1event0010",
"type": "review.requested",
"from_state": "submitted",
"to_state": "under_review",
"occurred_at": "2026-07-19T01:11:00Z"
},
{
"sequence": 12,
"event_id": "EVT-m1event0012",
"type": "decision.recorded",
"from_state": "under_review",
"to_state": "accepted",
"occurred_at": "2026-07-19T01:13:00Z"
}
],
"risk": "moderate",
"owner": {
"id": "model:builder",
"type": "model",
"role": "implementation"
},
"reviewer_count": 1,
"evidence_count": 0,
"review_count": 0,
"decision_count": 0
"evidence_count": 2,
"review_count": 2,
"decision_count": 1
}
]
}
5 changes: 5 additions & 0 deletions apps/console/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ h2 { font-size: clamp(28px, 4vw, 44px); letter-spacing: -.035em; margin: 8px 0 0
.task-title small { color: var(--muted); font-family: ui-monospace, monospace; margin-top: 5px; }
.badge { border: 1px solid var(--line); border-radius: 999px; display: inline-flex; font-family: ui-monospace, monospace; font-size: 10px; padding: 6px 9px; text-transform: uppercase; width: fit-content; }
.badge-state { border-color: rgba(121, 168, 255, .4); color: var(--blue); }
.badge-policy-failed { border-color: rgba(255, 107, 53, .5); color: var(--orange); }
.badge-risk-high, .badge-risk-critical { border-color: rgba(255, 107, 53, .5); color: var(--orange); }
.task-gates { display: flex; flex-wrap: wrap; gap: 7px; grid-column: 1 / -1; padding-bottom: 14px; }
.gate { border: 1px solid var(--line); border-radius: 999px; font-family: ui-monospace, monospace; font-size: 9px; letter-spacing: .04em; padding: 5px 8px; text-transform: uppercase; }
.gate-pass { border-color: rgba(185, 247, 124, .32); color: var(--lime); }
.gate-fail { border-color: rgba(255, 107, 53, .5); color: var(--orange); }
.load-error { background: rgba(255, 107, 53, .09); border: 1px solid rgba(255, 107, 53, .35); color: #ffc2ac; padding: 16px; }
.flow { counter-reset: relay; display: grid; gap: 1px; grid-template-columns: repeat(4, 1fr); list-style: none; margin: 0; padding: 0; }
.flow li { background: var(--panel); min-height: 210px; padding: 22px; }
Expand Down
Loading