@seed-hypermedia/cli
v0.2.13
Published
Command-line interface for Seed Hypermedia
Readme
Seed Hypermedia CLI
A command-line interface for interacting with the Seed Hypermedia network. Create, read, update, and delete documents; manage accounts, comments, contacts, and capabilities; sign content with Ed25519 keys — all from your terminal.
Package: @seed-hypermedia/cli License: MIT Requires: Node.js >= 18.0.0
Table of Contents
- Installation
- Quick Start
- Global Options
- Configuration
- Document Commands
- Account Commands
- Comment Commands
- Contact Commands
- Capability Commands
- Key Management Commands
- Search & Discovery Commands
- Hypermedia ID Format
- Output Formats
- Markdown Conversion
- Key Storage & Cryptography
- Signing & Blob Architecture
- Environment Variables
- Error Handling
- Scripting Examples
- Development
Installation
# Install globally via npm
npm install -g @seed-hypermedia/cli
# Or run directly with npx (no install needed)
npx -y @seed-hypermedia/cli --helpAfter installation, the CLI is available as both seed-cli and seed-hypermedia.
Development Mode
cd frontend/apps/cli
# Run directly with bun
bun run src/index.ts [command]
# Or use the dev script
bun run dev [command]Quick Start
# 1. Generate a signing key
seed-cli key generate -n mykey --show-mnemonic
# 2. Create a document
seed-cli document create z6MkrbYs... --title "My First Document" --body "Hello, world!"
# 3. Read it back
seed-cli document get hm://z6MkrbYs.../my-first-document --md
# 4. Search for content
seed-cli search "hello world"
# 5. List all accounts
seed-cli account listGlobal Options
These options apply to every command and must appear before the subcommand:
| Option | Description |
| -------------------- | ------------------------------------------------------------- |
| -s, --server <url> | Server URL (default: https://hyper.media or $SEED_SERVER) |
| --json | JSON output (default) |
| --yaml | YAML output |
| --pretty | Pretty-formatted colorized output |
| -q, --quiet | Minimal output (tab-separated values for scripting) |
| --dev | Use development keyring (seed-daemon-dev) |
| -V, --version | Display version number |
| -h, --help | Display help |
Examples:
# Use a different server
seed-cli --server http://localhost:4000 account list
# Get YAML output
seed-cli account list --yaml
# Pretty-printed colorized JSON
seed-cli account get z6Mk... --pretty
# Quiet mode for piping
seed-cli account list -q | head -5
# Use development keyring
seed-cli --dev key listConfiguration
The CLI stores configuration in ~/.seed/config.json (mode 0600).
config
Manage CLI configuration.
| Option | Description |
| ---------------- | -------------------------- |
| --server <url> | Set default server URL |
| --show | Show current configuration |
# Show current config
seed-cli config --show
# Set default server
seed-cli config --server https://my-server.example.comConfig fields:
| Field | Description |
| ---------------- | ------------------------------------- |
| server | Default server URL |
| defaultAccount | Account ID of the default signing key |
Document Commands
All document commands live under seed-cli document <subcommand>.
document get
Fetch a document, comment, or entity by Hypermedia ID.
seed-cli document get <id> [options]| Option | Description |
| ---------------- | ------------------------------------------------------- |
| -m, --metadata | Fetch metadata only (faster, skips content) |
| --md | Output as Markdown |
| --frontmatter | Include YAML frontmatter (requires --md) |
| -r, --resolve | Resolve embeds, mentions, and queries (requires --md) |
| -q, --quiet | Print document title only (or ID if untitled) |
Examples:
# Get full document as JSON
seed-cli document get hm://z6Mk.../my-doc
# Get as Markdown
seed-cli document get hm://z6Mk.../my-doc --md
# Get as Markdown with YAML frontmatter and resolved embeds
seed-cli document get hm://z6Mk.../my-doc --md --frontmatter --resolve
# Get metadata only (name, summary, etc.)
seed-cli document get hm://z6Mk.../my-doc -m
# Get a specific version
seed-cli document get "hm://z6Mk.../my-doc?v=bafy2bzace..."
# Get a nested child document
seed-cli document get hm://z6Mk.../projects/alpha
# Get just the title
seed-cli document get hm://z6Mk.../my-doc -q
# Also works for comments
seed-cli document get "hm://z6Mk...?c=bafy..."Behavior:
- When the resource is a
document, outputs document content. - When the resource is a
comment, outputs comment content. - With
--md, converts block content to GitHub-flavored Markdown. - With
--md --resolve, fetches embedded documents and query results, inlining their content. - With
--metadata, fetches only the lightweight metadata payload.
document create
Create a new document from Markdown or HMBlockNodes JSON.
seed-cli document create <account> [options]| Option | Description |
| ---------------------- | ---------------------------------------- |
| --title <title> | Required. Document title |
| -p, --path <path> | Document path (default: slugified title) |
| --body <text> | Markdown content (inline) |
| --body-file <file> | Read Markdown content from a file |
| --blocks <json> | HMBlockNodes JSON (inline) |
| --blocks-file <file> | Read HMBlockNodes JSON from a file |
| -k, --key <name> | Signing key name or account ID |
Constraints:
- Exactly one content source is required:
--body,--body-file,--blocks, or--blocks-file. --body/--body-fileand--blocks/--blocks-fileare mutually exclusive.- The
--titleoption is required.
Examples:
# Create with inline Markdown
seed-cli document create z6Mk... --title "My Article" --body "# Introduction\n\nHello!"
# Create from a Markdown file
seed-cli document create z6Mk... --title "My Article" --body-file article.md
# Create from HMBlockNodes JSON
seed-cli document create z6Mk... --title "Structured Doc" --blocks-file blocks.json
# Create with custom path
seed-cli document create z6Mk... --title "My Article" --path "articles/2024/my-article" --body "content"
# Create with a specific signing key
seed-cli document create z6Mk... --title "My Article" --body "content" --key authorUnder the hood:
- Creates a genesis change blob (empty, signed).
- Creates a document change blob containing the title and content operations.
- Creates a version ref blob pointing to the change.
- Publishes all three blobs to the server.
Output:
✓ Document created: hm://z6Mk.../my-article
ℹ Title: My Article
ℹ Path: /my-article
ℹ Genesis CID: bafy2bzace...
ℹ Change CID: bafy2bzace...document update
Update document metadata, append content, replace content, or delete blocks.
seed-cli document update <id> [options]| Option | Description |
| ----------------------- | -------------------------------------------------------------------------------------- |
| -f, --file <path> | Input file (format detected by extension: .md, .json). Diffs against existing content. |
| --title <title> | Set document title |
| --summary <summary> | Set document summary |
| --parent <blockId> | Parent block ID for new content (default: root) |
| --delete-blocks <ids> | Comma-separated block IDs to delete |
| -k, --key <name> | Signing key name or account ID |
Constraints:
- At least one update option must be specified.
Examples:
# Update the title
seed-cli document update hm://z6Mk.../my-doc --title "New Title"
# Update content from a Markdown file (smart diff — only changed blocks are submitted)
seed-cli document update hm://z6Mk.../my-doc -f updated.md
# Delete specific blocks
seed-cli document update hm://z6Mk.../my-doc --delete-blocks "blk-abc123,blk-def456"
# Combine metadata and content updates
seed-cli document update hm://z6Mk.../my-doc --title "Updated" --summary "A summary" -f content.mdSmart diff (-f):
When -f is used, the CLI performs a per-block diff against the existing document:
- Each input block's ID is checked against the old document's block map.
- If the ID exists: content is compared, and only changed blocks get a
ReplaceBlockop. - If the ID doesn't exist: the block is treated as new (emits
ReplaceBlock). - Old blocks whose IDs are absent from the input are deleted (
DeleteBlocks). - Block ordering is updated via
MoveBlocks.
This means editing a document exported with document get (which includes <!-- id:... --> comments in Markdown, or
block IDs in JSON) produces minimal updates. Plain Markdown without ID comments results in a full body replacement,
since none of the generated IDs will match the existing document.
document delete
Delete a document by publishing a tombstone ref.
seed-cli document delete <id> [options]| Option | Description |
| ------------------ | ------------------------------ |
| -k, --key <name> | Signing key name or account ID |
seed-cli document delete hm://z6Mk.../my-doc --key authorThe document isn't physically removed — a tombstone ref is published, which tells clients the document has been intentionally deleted.
document fork
Fork a document to a new location (creates a copy).
seed-cli document fork <sourceId> <destinationId> [options]| Option | Description |
| ------------------ | ------------------------------ |
| -k, --key <name> | Signing key name or account ID |
seed-cli document fork hm://z6Mk.../original hm://z6Mk.../copy --key authorBehavior:
- Creates a version ref at the destination pointing to the source's genesis and version.
- Does not create a redirect at the source (unlike
move). - The fork is an independent copy that shares the same genesis.
document move
Move a document to a new location (fork + redirect at source).
seed-cli document move <sourceId> <destinationId> [options]| Option | Description |
| ------------------ | ------------------------------ |
| -k, --key <name> | Signing key name or account ID |
seed-cli document move hm://z6Mk.../old-path hm://z6Mk.../new-path --key authorBehavior:
- Creates a version ref at the destination (same as
fork). - Creates a redirect ref at the source pointing to the destination.
- Clients following the source URL will be transparently redirected.
document redirect
Create a redirect from one document to another.
seed-cli document redirect <id> [options]| Option | Description |
| ------------------ | ------------------------------------------------- |
| --to <targetId> | Required. Target Hypermedia ID to redirect to |
| --republish | Republish target content at this location |
| -k, --key <name> | Signing key name or account ID |
# Simple redirect
seed-cli document redirect hm://z6Mk.../old-page --to hm://z6Mk.../new-page
# Redirect with content republishing
seed-cli document redirect hm://z6Mk.../mirror --to hm://z6Mk.../original --republishdocument changes
List document change history (version DAG).
seed-cli document changes <targetId> [options]| Option | Description |
| ------------- | -------------------------------- |
| -q, --quiet | Output CID<tab>author per line |
# Full change history
seed-cli document changes hm://z6Mk.../my-doc
# Compact: CID and author per line
seed-cli document changes hm://z6Mk.../my-doc -qOutput includes:
- Change ID (CID)
- Author account ID
- Dependencies (parent changes in the DAG)
- Create time
- Latest version CID
document stats
Get interaction statistics for a document.
seed-cli document stats <id>seed-cli document stats hm://z6Mk.../my-docOutput includes:
- Citation count (incoming links)
- Comment count
- Change count (edit history)
- Child document count
- Per-block citation and comment counts
document cid
Fetch a raw IPFS block by its CID.
seed-cli document cid <cid>seed-cli document cid bafy2bzace...Returns the decoded IPLD block data as JSON.
Space Commands (repo HM sync)
Mirror a whole space to a directory of markdown files in a repository and back. The markdown is the lossless dialect of
@seed-hypermedia/client (blocksToMarkdown / parseMarkdown): every block type, annotation, attribute and metadata
key survives a round trip, block ids ride in trailing <!-- id:… --> comments, and links between documents of the space
are written as relative file links. The repository's hypermedia/ folder is published this way from CI (sync-hypermedia.ts).
space export
seed-cli space export <space> --dir <path>Writes every document of the space into the directory: the home document as index.md, a document at /a/b as
a/b.md, and the schema blob a document defines (metadata.schemaDefinition) as <file>.schema.json. Only files whose
content changed are touched. <space> may be self for the signing key's own space.
The --assets flag also downloads every file the documents link (images, videos, files) into <dir>/assets/, named by
CID, and rewrites those links to relative paths. space import uploads them again.
space archive
seed-cli space archive <space> --out <file.zip> [--format markdown|blobs] [--no-comments]Saves a whole space in one zip file, with a manifest.json at its root.
markdown(default): thespace exportfolder plus its assets. Readable without Seed, and it can be imported into any space.blobs: every signed blob of the space atblobs/<cid>: the Refs at each document path, every change, capabilities, comments (unless--no-comments), schema blobs, and the UnixFS blocks of every linked file. Blobs are followed by their links, so the archive is self-contained, and each one is checked against its CID. Refs need a server with theListRefsAPI; an older server gives an archive with a warning and no Refs.
Anything linked that the server can't return is listed under missing in the manifest. A moved document's old path (its
Redirect Ref) is not archived yet.
space restore
seed-cli space restore <file.zip> [--into <space>] [-k, --key <name>] [-d, --dir <path>] [--dry-run]A blob archive is checked against its CIDs and published as is, in dependency order, to the --server. Nothing is
re-signed, so the space comes back with the same versions, authors and history. A markdown archive is extracted (to
--dir, or a temporary directory) and imported into --into (default self), which publishes new changes signed by
your key.
space import
seed-cli space import <space> --dir <path> [--dry-run] [-k, --key <name>]Publishes the directory into the space. Each file is diffed by block id against the current document and published as a
change on its existing history; unchanged documents produce nothing. A file without ids is matched to the existing
document by position. In CI, sign without a stored key by setting SEED_CLI_KEYFILE to the contents of an unencrypted
.hmkey.json (or SEED_CLI_MNEMONIC to a BIP-39 phrase) and use self as the space.
space dev
seed-cli space dev --dir <path> [--api <url>] [--daemon <url>] [--interval <ms>] [--no-push]Edit the directory in the desktop dev app: a throwaway key is created under <path>/.dev/ (self-ignored) and registered
in the app's daemon, the directory is published there, and every document published in the app is written straight back
to the directory. Nothing reaches the network. In this repository, ./dev hm-sync [dir] runs it.
Account Commands
All account commands live under seed-cli account <subcommand>.
account get
Get account information.
seed-cli account get <uid> [options]| Option | Description |
| ------------- | --------------------------------------- |
| -q, --quiet | Print name only (or UID if no name set) |
# Full account info
seed-cli account get z6MkrbYs...
# Pretty print
seed-cli account get z6MkrbYs... --pretty
# Just the name
seed-cli account get z6MkrbYs... -qaccount list
List all known accounts on the server.
seed-cli account list [options]| Option | Description |
| ------------- | ----------------------------- |
| -q, --quiet | Output ID<tab>name per line |
# Full JSON
seed-cli account list
# Compact for scripting
seed-cli account list -qaccount contacts
List contacts for an account.
seed-cli account contacts <uid> [options]| Option | Description |
| ------------- | ----------------- |
| -q, --quiet | Output names only |
seed-cli account contacts z6Mk...
seed-cli account contacts z6Mk... -qaccount profile set
Publish a signed Profile blob for an account. This is the current standard location for account display information such as name, icon/avatar, and description.
seed-cli account profile set --name <name> [options]| Option | Description |
| ----------------------- | ------------------------------------------------ |
| --name <value> | Required. Profile display name |
| --icon <value> | Profile icon/avatar URI (ipfs://...) |
| --avatar <value> | Alias for --icon |
| --description <value> | Short profile description (less than 512 bytes) |
| -a, --account <uid> | Target account UID (defaults to the signing key) |
| -k, --key <name> | Signing key name or account ID |
| -q, --quiet | Output CID only |
# Set the signing key's own profile
seed-cli account profile set --name "Publishing Bot" --description "Automated publisher" --key bot
# Set an account profile using an agent key with an AGENT capability
seed-cli account profile set --account z6Mk... --name "Team Account" --key agent
# Script-friendly output
seed-cli account profile set --name "Publishing Bot" --key bot --quietWith --json/--yaml, output includes the published profile blob CID, target account, and submitted profile fields.
account capabilities
List access control capabilities for a resource.
seed-cli account capabilities <id>seed-cli account capabilities hm://z6Mk...Output includes: capability ID, issuer, delegate, role, path restrictions, recursive flag.
Comment Commands
All comment commands live under seed-cli comment <subcommand>.
comment get
Get a single comment by ID.
seed-cli comment get <id>seed-cli comment get "hm://z6Mk...?c=bafy..."comment list
List all comments on a document.
seed-cli comment list <targetId> [options]| Option | Description |
| ------------- | ----------------------------------- |
| -q, --quiet | Output ID<tab>authorName per line |
seed-cli comment list hm://z6Mk.../my-doc
seed-cli comment list hm://z6Mk.../my-doc -qcomment create
Create a comment on a document.
seed-cli comment create <targetId> [options]| Option | Description |
| --------------------- | ---------------------------------------- |
| --body <text> | Comment text (inline) |
| --file <path> | Read comment text from file |
| --reply <commentId> | Reply to an existing comment (threading) |
| -k, --key <name> | Signing key name or account ID |
Examples:
# Top-level comment
seed-cli comment create hm://z6Mk.../my-doc --body "Great article!" --key reviewer
# Reply to a comment (creates a thread)
seed-cli comment create hm://z6Mk.../my-doc --body "Thanks!" --reply bafy... --key author
# Comment on a specific block (block-level annotation)
seed-cli comment create "hm://z6Mk.../my-doc#blk-abc123" --body "This paragraph needs work" --key editor
# Read comment from file
seed-cli comment create hm://z6Mk.../my-doc --file review.txt --key reviewerInline mentions:
Comment text supports inline mentions using the format @[DisplayName](hm://accountId). Mentions are converted to
Embed annotations with a U+FFFC object replacement character.
seed-cli comment create hm://z6Mk.../doc --body "cc @[Alice](hm://z6MkAlice...)" --key bobBlock-level comments:
When the target ID includes a #blockId fragment, the comment body is automatically wrapped in an Embed block
referencing the specific block. This matches the behavior of the desktop and web apps for block-level annotations.
Threading:
When --reply is specified, the CLI fetches the parent comment to determine the thread root. If the parent is already a
reply, the thread root is inherited. Otherwise, the parent comment itself becomes the thread root.
comment edit
Edit an existing comment.
seed-cli comment edit <commentId> [options]| Option | Description |
| ------------------ | ------------------------------ |
| --body <text> | Updated comment text (inline) |
| --file <path> | Read updated text from file |
| -k, --key <name> | Signing key name or account ID |
Examples:
# Edit inline
seed-cli comment edit z6MkAuthor.../zb2rComment... --body "Updated wording" --key reviewer
# Edit from file
seed-cli comment edit z6MkAuthor.../zb2rComment... --file review-update.txt --key reviewerThe CLI preserves reply threading metadata for replies and keeps the block-level anchor wrapper for comments that were originally attached to a specific block.
comment delete
Delete a comment by publishing a tombstone.
seed-cli comment delete <commentId> [options]| Option | Description |
| ------------------ | ------------------------------ |
| -k, --key <name> | Signing key name or account ID |
seed-cli comment delete bafy... --key reviewercomment discussions
List threaded discussions on a document.
seed-cli comment discussions <targetId> [options]| Option | Description |
| -------------------- | --------------------------- |
| -c, --comment <id> | Filter to a specific thread |
# All discussions
seed-cli comment discussions hm://z6Mk.../my-doc
# A specific thread
seed-cli comment discussions hm://z6Mk.../my-doc --comment bafy...Contact Commands
All contact commands live under seed-cli contact <subcommand>.
contact create
Create a contact (a named reference to another account).
seed-cli contact create [options]| Option | Description |
| ----------------------- | ------------------------------------------ |
| --subject <accountId> | Required. Account ID being described |
| --name <name> | Required. Display name for the contact |
| -k, --key <name> | Signing key name or account ID |
seed-cli contact create --subject z6MkAlice... --name "Alice" --key mykeyOutput: Contact record ID in authority/tsid format.
contact delete
Delete a contact by publishing a tombstone.
seed-cli contact delete <contactIdOrCid> [options]| Option | Description |
| ------------------ | ------------------------------ |
| -k, --key <name> | Signing key name or account ID |
Accepts either a record ID (authority/tsid) or a CID. If given a CID, the CLI fetches the blob to compute the record
ID.
seed-cli contact delete z6Mk.../zQ3sh... --key mykey
seed-cli contact delete bafy... --key mykeycontact list
List contacts for an account.
seed-cli contact list [accountId] [options]| Option | Description |
| ----------- | --------------------------------------------------- |
| --account | Only show contacts signed by the account |
| --subject | Only show contacts where the account is the subject |
By default, shows contacts in both directions (signed by and about the account).
# Both directions
seed-cli contact list z6Mk...
# Only contacts this account created
seed-cli contact list z6Mk... --account
# Only contacts about this account
seed-cli contact list z6Mk... --subjectCapability Commands
All capability commands live under seed-cli capability <subcommand>.
capability create
Create a capability (delegate access to another account).
seed-cli capability create [options]| Option | Description |
| ------------------------ | --------------------------------------- |
| --delegate <accountId> | Required. Account receiving access |
| --role <role> | Required. Role: WRITER or AGENT |
| --path <path> | Path scope for the capability |
| --label <label> | Human-readable label |
| -k, --key <name> | Signing key name or account ID |
# Grant full write access
seed-cli capability create --delegate z6MkBob... --role WRITER --key alice
# Grant write access scoped to a path
seed-cli capability create --delegate z6MkBot... --role AGENT --path /blog --label "Blog bot" --key aliceKey Management Commands
All key commands live under seed-cli key <subcommand>. The keys alias also works.
Keys come from two sources:
- The vault (
vault.json) — the encrypted identity store of the Seed desktop app and daemon. The CLI auto-detects it (or use--vault <path>/SEED_VAULT_PATH/seed-cli config --vault-path), unlocks it with the secret in the OS keychain (orSEED_VAULT_KEKon headless machines), and can sign with any identity in it. Read-only: the CLI never modifies the vault. - The OS keyring (macOS Keychain / Linux libsecret) — the legacy store, still used for keys the CLI creates itself.
The vault wins when a name exists in both. key list shows each key's source. See docs/KEYS.md for details.
key generate
Generate a new signing key from a BIP-39 mnemonic.
seed-cli key generate [options]| Option | Description |
| --------------------- | ------------------------------------------------- |
| -n, --name <name> | Key name (default: main) |
| -w, --words <count> | Mnemonic word count: 12 or 24 (default: 12) |
| --passphrase <pass> | Optional BIP-39 passphrase |
| --show-mnemonic | Display the mnemonic (save it securely!) |
# Generate with default settings
seed-cli key generate
# Generate a named key with 24 words and show the mnemonic
seed-cli key generate -n secure-key --words 24 --show-mnemonic
# Generate with passphrase
seed-cli key generate -n protected --passphrase "my secret"Key name rules: Only alphanumeric characters, hyphens, and underscores.
key import
Import a key from an existing BIP-39 mnemonic.
seed-cli key import <mnemonic> [options]| Option | Description |
| --------------------- | ------------------------------ |
| -n, --name <name> | Key name (default: imported) |
| --passphrase <pass> | Optional BIP-39 passphrase |
seed-cli key import -n restored "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
seed-cli key import -n restored --passphrase "secret" "word1 word2 ... word12"key list
List all stored signing keys.
seed-cli key list| Option | Description |
| ------------- | ------------------------------------ |
| -q, --quiet | Output name<tab>accountId per line |
seed-cli key list
seed-cli key list -qkey show
Show information for a specific key, or the default key if none specified.
seed-cli key show [nameOrId]Accepts a key name or account ID.
# Show default key
seed-cli key show
# Show by name
seed-cli key show main
# Show by account ID
seed-cli key show z6Mk...key default
Set or show the default signing key.
seed-cli key default [nameOrId]# Show current default
seed-cli key default
# Set a new default
seed-cli key default mykeyDefault key resolution order:
- Config
defaultAccount(set bykey default) — vault first, then keyring - Key named
main— vault first, then keyring - First vault account
- First key in the keyring
key remove
Remove a stored key.
seed-cli key remove <nameOrId> [options]| Option | Description |
| ------------- | ----------------- |
| -f, --force | Skip confirmation |
# Shows warning, requires --force
seed-cli key remove mykey
# Actually remove
seed-cli key remove mykey --forcekey rename
Rename a stored key.
seed-cli key rename <currentName> <newName>seed-cli key rename old-name new-namekey derive
Derive an account ID from a mnemonic without storing the key.
seed-cli key derive <mnemonic> [options]| Option | Description |
| --------------------- | -------------------------- |
| --passphrase <pass> | Optional BIP-39 passphrase |
seed-cli key derive "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
# Output: z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWpUseful for verifying a mnemonic maps to the expected account without writing it to the keyring.
Search & Discovery Commands
These are top-level commands (not subcommands).
search
Search for documents across the network.
seed-cli search <query> [options]| Option | Description |
| --------------------- | ---------------------------------- |
| -a, --account <uid> | Limit search to a specific account |
| -q, --quiet | Output ID<tab>title per line |
seed-cli search "artificial intelligence"
seed-cli search "meeting notes" --account z6Mk...
seed-cli search "project" -qquery
List documents in a space (account).
seed-cli query <space> [options]| Option | Description |
| ------------------- | ------------------------------------------------------------------------- |
| -p, --path <path> | Path prefix to filter |
| -m, --mode <mode> | Children (default) or AllDescendants |
| -l, --limit <n> | Maximum results |
| --sort <term> | Sort by: title, path, created, updated, activity, displayTime |
| --reverse | Reverse sort order |
| -q, --quiet | Output ID<tab>name per line |
# List direct children
seed-cli query z6Mk...
# List all descendants recursively
seed-cli query z6Mk... --mode AllDescendants
# List children under a specific path
seed-cli query z6Mk... --path projects
# Sort by update time, newest first
seed-cli query z6Mk... --sort UpdateTime --reverse
# Limit to 5 results
seed-cli query z6Mk... --limit 5children
Shorthand for query --mode Children.
seed-cli children <space> [options]| Option | Description |
| ------------------- | ----------------------------- |
| -p, --path <path> | Path prefix |
| -l, --limit <n> | Limit results |
| -q, --quiet | Output ID<tab>name per line |
# These are equivalent:
seed-cli children z6Mk...
seed-cli query z6Mk... --mode Childrencitations
List documents that cite (link to) a given resource.
seed-cli citations <id> [options]| Option | Description |
| ------------- | ---------------------- |
| -q, --quiet | Output source IDs only |
seed-cli citations hm://z6Mk.../my-doc
seed-cli citations hm://z6Mk.../my-doc -qactivity
List recent activity events across the network.
seed-cli activity [options]| Option | Description |
| --------------------- | --------------------------------------- |
| -l, --limit <n> | Page size |
| -t, --token <token> | Pagination token (from previous output) |
| --authors <uids> | Filter by author UIDs (comma-separated) |
| --resource <id> | Filter by resource |
| -q, --quiet | Output event count + next token only |
# Recent activity
seed-cli activity
# Paginate
seed-cli activity --limit 20
seed-cli activity --limit 20 --token <nextPageToken>
# Filter by author
seed-cli activity --authors z6Mk...
# Filter by resource
seed-cli activity --resource hm://z6Mk.../my-docHypermedia ID Format
Seed uses Hypermedia IDs (HM IDs) as universal resource identifiers:
hm://<uid>[/<path>][?v=<version>][?c=<commentCID>][#<blockRef>]| Component | Description | Example |
| ------------ | ------------------------------------- | -------------------- |
| uid | Account's Ed25519 public key ID | z6MkrbYsRzKb1VA... |
| path | Forward-slash-separated path segments | /projects/alpha |
| version | CID of a specific document version | ?v=bafy2bzace... |
| commentCID | CID of a comment | ?c=bafy2bzace... |
| blockRef | Specific block ID within the document | #blk-abc123 |
Examples:
hm://z6MkrbYsRzKb1VABdvhsDSAk6JK8fAszKsyHhcaZigYeWCou
hm://z6MkrbYs.../projects/alpha
hm://z6MkrbYs.../my-doc?v=bafy2bzacedfvn...
hm://z6MkrbYs.../my-doc#blk-abc123
hm://z6MkrbYs...?c=bafy2bzace...The CLI also accepts bare UIDs (without the hm:// prefix) in most places.
Output Formats
JSON (default)
Pretty-printed with 2-space indentation. BigInt values are serialized as strings.
seed-cli account list{
"accounts": [
{
"id": {"id": "hm://z6Mk...", "uid": "z6Mk..."},
"metadata": {"name": "Account Name"}
}
]
}YAML
seed-cli account list --yamlaccounts:
- id:
id: hm://z6Mk...
uid: z6Mk...
metadata:
name: Account NamePretty
Colorized JSON output:
- Green: strings
- Yellow: numbers
- Blue: booleans
- Cyan: object keys
- Dim gray: null
seed-cli account list --prettyQuiet
Tab-separated values, one record per line. Ideal for piping to awk, cut, grep, etc.
seed-cli account list -q
# hm://z6Mk... Account Name
# hm://z6Mk... Another AccountMarkdown Conversion
Document to Markdown (--md)
When using document get --md, the CLI converts the internal block tree to GitHub-flavored Markdown:
| Block type | Markdown rendering |
| ------------- | ----------------------------------------------------------- |
| Heading | #, ##, ###, etc. based on depth |
| Paragraph | Plain text with inline formatting |
| Code | Fenced code blocks with language tag |
| Math | $$...$$ LaTeX blocks |
| Image |  (IPFS CIDs converted to gateway URLs) |
| Embed | Blockquote with link (or inlined content with --resolve) |
| Query | Resolved to a list of linked documents (with --resolve) |
| Button | [text](url) link |
| Bold | **text** |
| Italic | *text* |
| Code (inline) | `text` |
| Link | [text](url) |
Frontmatter (--frontmatter):
---
title: Document Title
summary: A brief description
version: bafy2bzace...
authors:
- z6Mk...
---Resolve mode (--resolve):
Fetches embedded documents and query results from the server and inlines their content directly in the Markdown output, up to a default recursion depth of 2.
Markdown to Blocks (input)
When creating or updating documents with --body or --body-file, the CLI parses Markdown into the internal block
tree:
| Markdown element | Block type |
| -------------------- | ----------------------------------------------- |
| # Heading | Heading container with children |
| Paragraphs | Paragraph blocks |
| ```code``` | Code block with language attribute |
| **bold** | Bold annotation |
| *italic* | Italic annotation |
| `code` | Code annotation |
| [text](url) | Link annotation |
| - item / 1. item | Children with childrenType: Unordered/Ordered |
Key Storage & Cryptography
Where Keys Live
The desktop app and daemon keep identities in the encrypted vault (vault.json), which the CLI reads via
@seed-hypermedia/client/vault-local. Keys the CLI creates itself go to the OS keyring:
| Platform | Backend | CLI tool used |
| -------- | -------------------------------- | ------------- |
| macOS | Keychain | security |
| Linux | D-Bus Secret Service (libsecret) | secret-tool |
The same keychain also holds the vault's unlock secret (service seed-hypermedia-vault-secret-v2).
The keyring is shared with the Go daemon and desktop app. Keys registered in any Seed application are available in all others.
Keyring Service Names
| Environment | Service Name | Selected by |
| ----------- | ------------------ | ------------------- |
| Production | seed-daemon-main | Default |
| Development | seed-daemon-dev | --dev global flag |
Key Encoding Format
Each key is stored as a 68-byte libp2p protobuf-encoded Ed25519 key pair, base64-encoded:
[08 01 12 40] [32-byte private seed] [32-byte public key]
─── header ─── ─── 64 bytes of key data ───The 4-byte header is:
08 01— protobuf field 1 (key type) = 1 (Ed25519)12 40— protobuf field 2 (key data) length = 64 bytes
Account ID Derivation
32-byte Ed25519 public key
↓ prepend multicodec prefix [0xed, 0x01]
34-byte multicodec key
↓ base58btc encode (multibase prefix 'z')
Account ID string (starts with "z6Mk")Key Derivation from Mnemonic
BIP-39 Mnemonic + optional Passphrase
↓ bip39.mnemonicToSeedSync()
64-byte seed
↓ SLIP-10 derivation at path m/44'/104109'/0'
32-byte Ed25519 private key
↓ ed25519.getPublicKey()
32-byte Ed25519 public key
↓ computeAccountId()
Account ID (z6Mk...)The derivation path 104109 is the Unicode codepoint sum for h + m (104 + 109).
Cross-Format Compatibility
The keyring JSON payload uses two formats:
- Plain JSON — written by the CLI
go-keyring-base64:<base64>— written by the Go daemon
The CLI reads both formats transparently. When writing, it uses the go-keyring-base64: format for compatibility with
the daemon.
Signing & Blob Architecture
All mutable data in Seed is stored as signed CBOR blobs. The CLI creates and signs these blobs client-side before publishing them to the server.
Signing Process
- Create the unsigned object with
sig: new Uint8Array(64)(zeroed) andts: 0n. - CBOR-encode the unsigned object.
- Sign the CBOR bytes with Ed25519.
- Fill in the actual
sigandtsvalues. - CBOR-encode the final signed object.
- Compute the SHA-256 CID of the encoded bytes.
- Publish with the explicit CID.
Blob Types
Genesis Change: The root of every document's change DAG.
{type: "Change", signer, sig, ts: 0n}Document Change: Mutations to document content and metadata.
{type: "Change", signer, sig, ts, genesis: CID, deps: CID[], depth, body: {ops, opCount}}Version Ref: Points clients to the current document head.
{type: "Ref", signer, sig, ts, space, path, genesis, version, generation}Tombstone Ref: Marks a document as deleted.
Redirect Ref: Points one document to another.
Comment: A signed comment blob attached to a document version.
Contact: A named reference from one account to another.
Capability: A delegation of write or agent access.
Document Operations
Changes contain arrays of operations:
| Operation | Description |
| --------------- | ------------------------------ |
| SetAttributes | Set metadata key-value pairs |
| ReplaceBlock | Insert or replace a block |
| MoveBlocks | Move blocks under a new parent |
| DeleteBlocks | Remove blocks by ID |
Environment Variables
| Variable | Description |
| ------------- | ---------------------------------------------------- |
| SEED_SERVER | Default server URL (overrides https://hyper.media) |
Error Handling
Exit codes:
| Code | Meaning |
| ---- | ----------------------------------------------------- |
| 0 | Success |
| 1 | Error (invalid input, network error, not found, etc.) |
Error output: Errors are printed to stderr with a red ✗ prefix.
Common errors:
| Error | Cause |
| --------------------------------------------------------------- | -------------------------------------------------- |
| Invalid Hypermedia ID | Malformed hm:// URL |
| API error (500): ... | Server-side error or resource not found |
| Key "name" not found | No key with that name or account ID in the keyring |
| No signing keys found | Must run key generate or key import first |
| Cannot combine --body/--body-file with --blocks/--blocks-file | Mutually exclusive options |
Scripting Examples
Export all documents as Markdown
#!/bin/bash
ACCOUNT="z6MkrbYsRzKb1VABdvhsDSAk6JK8fAszKsyHhcaZigYeWCou"
seed-cli query "$ACCOUNT" --mode AllDescendants -q | while IFS=$'\t' read -r id name; do
filename=$(echo "$name" | tr ' /' '_' | tr -cd '[:alnum:]_-')
[ -z "$filename" ] && filename="unnamed_$(echo "$id" | md5sum | head -c 8)"
seed-cli document get "$id" --md --resolve > "${filename}.md"
echo "Exported: ${filename}.md"
doneCreate a document from a Markdown file
#!/bin/bash
ACCOUNT="z6Mk..."
TITLE="$(head -1 "$1" | sed 's/^# //')"
seed-cli document create "$ACCOUNT" \
--title "$TITLE" \
--body-file "$1" \
--key mainBatch update documents from local files
#!/bin/bash
for file in docs/*.md; do
DOC_PATH=$(basename "$file" .md)
ID="hm://z6Mk.../$DOC_PATH"
echo "Updating $ID from $file"
seed-cli document update "$ID" -f "$file" --key main
doneMonitor activity feed
#!/bin/bash
TOKEN=""
while true; do
if [ -n "$TOKEN" ]; then
OUTPUT=$(seed-cli activity --limit 10 --token "$TOKEN")
else
OUTPUT=$(seed-cli activity --limit 10)
fi
echo "$OUTPUT" | jq '.events[] | "\(.eventType) by \(.author)"'
TOKEN=$(echo "$OUTPUT" | jq -r '.nextPageToken // empty')
sleep 60
doneFind all documents by a specific author
#!/bin/bash
AUTHOR="z6Mk..."
seed-cli activity --authors "$AUTHOR" --limit 100 | jq -r '.events[].resource' | sort -uDelegate write access
#!/bin/bash
seed-cli capability create \
--delegate z6MkBob... \
--role WRITER \
--key alice
seed-cli contact create \
--subject z6MkBob... \
--name "Bob" \
--key aliceBackup all keys (show mnemonics)
#!/bin/bash
seed-cli key list -q | while IFS=$'\t' read -r name id; do
echo "=== Key: $name ($id) ==="
seed-cli key show "$name"
echo ""
doneDevelopment
cd frontend/apps/cli
# Run directly
bun run src/index.ts [command]
# Build for distribution
npm run build
# Type check
npm run typecheck
# Run fixture-based tests (starts a local daemon)
bun test src/test/cli-fixture.test.ts
# Run daemon integration tests
bun test src/test/cli.test.ts
# Run live server tests
bun test src/test/cli-live.test.tsProject Structure
frontend/apps/cli/
├── src/
│ ├── index.ts # Entry point, global options, command registration
│ ├── client.ts # HTTP API client
│ ├── config.ts # ~/.seed/config.json management
│ ├── output.ts # Output formatting (JSON, YAML, pretty, table)
│ ├── markdown.ts # Document → Markdown conversion
│ ├── commands/
│ │ ├── document.ts # document get/create/update/delete/fork/move/redirect/changes/stats/cid
│ │ ├── comment.ts # comment get/list/create/delete/discussions
│ │ ├── capability.ts # capability create
│ │ ├── contact.ts # contact create/delete/list
│ │ ├── account.ts # account get/list/contacts/capabilities
│ │ ├── key.ts # key generate/import/list/show/default/remove/rename/derive
│ │ ├── search.ts # search
│ │ └── query.ts # query/children/citations/activity
│ ├── utils/
│ │ ├── keyring.ts # Cross-platform OS keyring access
│ │ ├── key-derivation.ts # BIP-39/SLIP-10 key derivation
│ │ ├── signing.ts # CBOR blob signing (genesis, changes)
│ │ ├── markdown.ts # Markdown → blocks parser
│ │ ├── blocks-json.ts # HMBlockNodes JSON parser
│ │ ├── block-diff.ts # Smart block diffing for document update
│ │ ├── depth.ts # Change DAG depth resolution
│ │ └── hm-id.ts # Hypermedia ID pack/unpack
│ └── test/
│ ├── cli-fixture.test.ts # Fixture-based integration tests
│ ├── cli.test.ts # Daemon integration tests
│ └── cli-live.test.ts # Live server tests
├── docs/
│ ├── API.md # HTTP API reference
│ ├── KEYS.md # Key management internals
│ ├── SIGNING.md # Signing and blob creation reference
│ └── TYPES.md # Type definitions reference
└── package.jsonDependencies
| Package | Purpose |
| ------------------------- | --------------------------- |
| commander | CLI framework |
| multiformats | CID/IPLD handling |
| @ipld/dag-cbor | CBOR encoding |
| @noble/ed25519 | Ed25519 signatures |
| @noble/hashes | Cryptographic hashing |
| @exodus/slip10 | SLIP-10 key derivation |
| bip39 | BIP-39 mnemonics |
| chalk | Terminal colors |
| yaml | YAML formatting |
| @seed-hypermedia/client | SDK for document operations |
Further Reading
- API Reference — HTTP API endpoints
- Key Management — Keyring internals and cross-platform storage
- Signing Reference — Blob signing and creation details
- Types Reference — Complete type definitions
- Seed Hypermedia — Project website
- GitHub Repository — Source code
