|
Some checks failed
control plane / chart (push) Has been cancelled
control plane / test (push) Has been cancelled
control plane / browser-e2e (push) Has been cancelled
control plane / Build control plane image (linux/amd64) (push) Has been cancelled
control plane / Build control plane image (linux/arm64) (push) Has been cancelled
control plane / Publish signed control plane image (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Meridian Harness / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Meridian Harness / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Meridian Harness / Publish rolling release (push) Has been cancelled
Meridian Harness / Publish tagged release (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Isolated DB Gate (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Admin Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Server Cross-Compile (push) Has been cancelled
CI / Server Cross-Compile-1 (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Live QA against a relay proved the engine works: a saved Welcome Greeter fired on a matching message and on a manual trigger, posting its reply both times. The desktop reported none of it. `get_workflow_runs` returns an empty vec unconditionally — run history lives in the relay's `workflow_runs` table and is exposed to no client — so the detail panel rendered a flat "No runs yet." after every trigger. That asserts an absence nothing verified, and NIP-WF already makes it a client MUST NOT. It is also why a working feature reads as broken. The panel now says run history is unavailable from this relay and that workflows still execute, and a trigger echoes the run id it started so a successful trigger produces a visible change. The mock bridge was the reason nobody caught this. `handleGetWorkflowRuns` synthesized a completed run with a full execution trace, so all 12 tests in workflows.spec.ts passed — including one asserting a rendered trace containing "step_1", which the product can never produce. The suite was green precisely where the feature was broken. The mock now returns what the command returns, and the trigger spec asserts the honest state instead. Recorded in desktop/tests/AGENTS.md as a rule: a mock never models behaviour its Tauri command cannot produce. Separately, webhook triggers are refused outright on a relay without MERIDIAN_WORKFLOW_SECRET_KEY, which takes the gallery's Deploy Approval template with them. The rejection read "invalid: workflow secret storage is not configured" — phrased as a defect in the definition the author had just written. It now names the deployment cause and says the definition is valid, the form states the requirement instead of promising a URL unconditionally, and .env.example says what leaving the key unset switches off. NIP-WF §Implementation Status and the CLI docstring both claimed run history was "surfaced to the desktop over local IPC". It is not, and never was. Verified: 12/12 workflows.spec.ts, desktop unit tests, tsc, biome, cargo fmt + clippy (workspace + Tauri), check-stellar. The relay message is a string change on a path this QA hit live; it is not yet re-probed against a rebuilt relay. Refs: meridian-do06, meridian-edtt, meridian-c7x1 Signed-off-by: Joshua Belke <joshua@innovationhub-act.org> |
||
|---|---|---|
| .. | ||
| src | ||
| AGENTS.md | ||
| Cargo.toml | ||
| README.md | ||
| TESTING.md | ||
Meridian CLI
Agent-first command-line interface for Meridian relay. JSON in, JSON out.
Install
cargo install --path crates/meridian-cli
Authentication
| Env Var | Mode | Use Case |
|---|---|---|
MERIDIAN_PRIVATE_KEY |
NIP-98 Schnorr signature | Agents with a keypair |
# Private key identity (NIP-98 signed requests)
export MERIDIAN_PRIVATE_KEY="nsec1..."
meridian channels list
Usage
All output is JSON on stdout. Errors are JSON on stderr. Exit codes: 0=ok, 1=user error, 2=network, 3=auth, 4=other, 5=write conflict.
# Set relay URL (defaults to http://localhost:3000)
export MERIDIAN_RELAY_URL="https://relay.example.com"
# Messages
meridian messages send --channel <uuid> --content "Hello"
meridian messages send --channel <uuid> --content "Reply" --reply-to <event-id> --broadcast
meridian messages send --channel <uuid> --content - < message.md # read body from stdin
meridian messages get --channel <uuid> --limit 20
meridian messages thread --channel <uuid> --event <event-id>
meridian messages search --query "architecture"
meridian messages search --author <pubkey|npub|name> --since <unix-ts>
meridian messages edit --event <event-id> --content "Updated text"
meridian messages delete --event <event-id>
# Diffs
meridian messages send-diff --channel <uuid> --diff - --repo https://github.com/org/repo --commit abc123 < diff.patch
# Channels
meridian channels list
meridian channels create --name "my-channel" --type stream --visibility open
meridian channels join --channel <uuid>
meridian channels topic --channel <uuid> --topic "New topic"
# Reactions
meridian reactions add --event <event-id> --emoji "👍"
meridian reactions get --event <event-id>
# Users & Presence
meridian users get # your own profile
meridian users get --pubkey <hex> # single user
meridian users get --pubkey <hex> --pubkey <hex> # batch (max 200)
meridian users set-presence --status online
meridian users set-status --text "heads down on the CLI" --emoji "🚀"
meridian users set-status --clear # remove your status
# DMs
meridian dms open --pubkey <hex>
meridian dms list
# Workflows
meridian workflows list --channel <uuid>
meridian workflows trigger --workflow <uuid>
meridian workflows approve --token <uuid>
meridian workflows approve --token <uuid> --approved false --note "needs revision"
# Forum
meridian messages vote --event <event-id> --direction up
# Canvas
meridian canvas get --channel <uuid>
meridian canvas set --channel <uuid> --content "# Welcome"
# Agent Memory (NIP-AE)
meridian mem ls
meridian mem get <slug>
meridian mem set <slug> "my-value"
meridian mem patch <slug> --base-hash <hex> < diff.patch # or --no-base-hash
meridian mem rm <slug>
# Repository protection
meridian repos protect list --id my-repo
meridian repos protect set --id my-repo --ref refs/heads/main --push admin --no-force-push --no-delete
meridian repos protect remove --id my-repo --ref refs/heads/main
# Pipe to jq
meridian channels list | jq '.[].name'
protect set replaces every existing rule for the exact ref pattern. Any
constraint omitted from the command is removed. protect list reports malformed
stored rules in validation_error so an owner can remove and repair them.
Commands
| Group | Subcommand | Description |
|---|---|---|
messages |
send |
Send a message to a channel |
send-diff |
Send a code diff with metadata | |
edit |
Edit a message you sent | |
delete |
Delete a message | |
get |
List messages in a channel | |
thread |
Get a message thread | |
search |
Full-text search, filterable by author | |
vote |
Vote on a forum post | |
channels |
list |
List channels |
get |
Get channel details | |
create |
Create a channel | |
update |
Update channel name/description | |
topic |
Set channel topic | |
purpose |
Set channel purpose | |
join |
Join a channel | |
leave |
Leave a channel | |
archive |
Archive a channel | |
unarchive |
Unarchive a channel | |
delete |
Delete a channel | |
members |
List channel members | |
add-member |
Add a member | |
remove-member |
Remove a member | |
canvas |
get |
Get channel canvas |
set |
Set channel canvas | |
reactions |
add |
React to a message |
remove |
Remove a reaction | |
get |
List reactions | |
dms |
list |
List DM conversations |
open |
Open a DM (1–8 pubkeys) | |
add-member |
Add member to DM group | |
users |
get |
Get user profile(s) |
set-profile |
Update your profile | |
presence |
Get presence status | |
set-presence |
Set presence status | |
set-status |
Set or clear your NIP-38 profile status | |
workflows |
list |
List workflows |
get |
Get workflow definition | |
create |
Create a workflow | |
update |
Update a workflow | |
delete |
Delete a workflow | |
trigger |
Trigger a workflow | |
runs |
Get workflow run history | |
approve |
Approve/deny a workflow step | |
feed |
get |
Get your activity feed |
social |
publish |
Publish a NIP-01 note |
set-contacts |
Set NIP-02 contact list | |
event |
Get a Nostr event | |
notes |
Get notes for a user | |
contacts |
Get NIP-02 contact list | |
repos |
create |
Announce a git repository (NIP-34) |
get |
Get a repository announcement | |
list |
List repository announcements | |
protect list |
List branch and tag protection rules | |
protect set |
Create or replace a protection rule | |
protect remove |
Remove a protection rule | |
upload |
file |
Upload a file to the Blossom store |
pack |
validate |
Validate a persona pack (local, no relay) |
inspect |
Inspect a persona pack (local, no relay) | |
mem |
ls |
List non-tombstoned memories |
get |
Print memory value to stdout | |
hash |
Print SHA-256 hex of memory value | |
set |
Write a memory value (use - for stdin) |
|
patch |
Apply unified diff to memory value | |
rm |
Publish a tombstone to delete memory |
Architecture
meridian <group> <subcommand> [flags]
│
├─ main.rs ──▶ commands/*.rs ──▶ client.rs ──▶ Meridian Relay REST API
│ (clap) (handlers) (reqwest)
│
├─ validate.rs (UUID, hex, content size, percent-encode)
└─ error.rs (CliError → JSON stderr + exit code)
stdout: raw relay JSON
stderr: {"error": "category", "message": "detail"}
exit: 0=ok 1=user 2=network 3=auth 4=other 5=write conflict