Contributing
How to find your way around the Seed repository, run the development stack, test and format each part, and ship changes to the daemon's storage, the protocol, releases and these docs.

Seed is one public repository, github.com/seed-hypermedia/seed, holding the Go daemon, the TypeScript apps and libraries, the agent runtime, the vault, deployment tooling, and the markdown for this site. This page is for people who want to change any of it. It covers the layout, the one-command development stack, the checks each area expects before a pull request, and the rules for the changes that are hard to undo: storage migrations, protocol changes and releases.

Every command below was checked against the repository on branch feat/onyx in September 2026. The repository also keeps instructions for coding agents in AGENTS.md at the root and in the subtrees listed below. They are written for humans too, and they are the authoritative version of the rules summarized here.

The repository

Path

What lives there

Toolchain

backend/

the Seed daemon: storage and indexing (storage/, blob/), the document model and CRDTs (api/documents/, crdt/), networking (hmnet/), gRPC APIs (api/), the embedding model (llm/)

Go, built with Please

proto/

protobuf definitions for every daemon service; generated Go and TypeScript are checked in

Please, ./dev gen

frontend/apps/desktop

the Seed app (Electron)

pnpm

frontend/apps/web

the Seed web app that serves sites, the Seed API and site services (Remix)

pnpm

frontend/apps/cli

seed-cli, also the home of the hypermedia/ sync script

pnpm workspace, runs under Bun

frontend/apps/notify

the email notification service

pnpm

frontend/apps/explore

the Hypermedia Explorer

pnpm

frontend/apps/mobile

the React Native app, outside the pnpm workspace

npm

frontend/apps/landing, emails, perf-web, performance, performance-dashboard

the landing site, email templates, performance tooling

pnpm

frontend/packages/client

@seed-hypermedia/client, the published SDK

pnpm

frontend/packages/shared, ui, editor

shared models and hooks (@shm/shared), UI components (@shm/ui), the editor (@shm/editor)

pnpm

agents/

the Seed Agents server

its own Bun workspace

vault/

the key vault service

its own Bun workspace

hypermedia/

this site: concept pages, the Hypermedia Schemas library, the Seed API reference, the Agents docs

markdown, scripts/hypermedia/

ops/

the self-hosted node deployment script (seed-deploy)

pinned Bun

tests/

cross-app integration tests

pnpm, Vitest, Playwright

docs/

internal design notes, decision records, runbooks and the security audit log; not published

markdown

agents/ and vault/ copy the frontend packages they use through file: dependencies, so a change to frontend/packages/* reaches them only after bun install. Their dev servers re-sync automatically. The map pages under The Seed software, such as Daemon, Desktop and Web, explain how the programs talk to each other.

Setting up

The toolchain versions are pinned in mise.toml and activated by direnv.

Install mise and direnv and hook direnv into your shell, then clone the repository and allow the environment. The first direnv allow installs the pinned tools, initializes the llama-go submodule, downloads the embedding model and builds the llama.cpp libraries, which takes a few minutes.

git clone https://github.com/seed-hypermedia/seed cd seed direnv allow pnpm install

Install Docker as well if you want the full stack, because the agents' web search backends run as containers.

./dev refuses to run outside a direnv-enabled shell. From scripts and agents, run commands as direnv exec . <command>.

Running things

./dev with no arguments lists every command. The ones you will use:

Command

What it does

./dev up

the whole stack on mainnet in one mprocs window, one pane per process (see mprocs.yaml)

./dev up-testnet

the same stack on the dev testnet (see mprocs.testnet.yaml)

./dev run-desktop

the desktop app, which builds and spawns its own daemon

./dev run-desktop-mainnet

the desktop app on mainnet

./dev run-backend

build and run seed-daemon alone; flags after -- go to the daemon

./dev build-backend, ./dev build-desktop, ./dev build-web

production builds

./dev test-desktop

the desktop test suite

./dev install-cli

build seed-cli and link it into ~/.local/bin

./dev hm-sync [dir]

edit a markdown folder, hypermedia/ by default, in the desktop dev app

./dev gen [targets]

check generated code and regenerate what is stale

./dev frontend-validate

format the whole workspace, then check formatting the way CI does

./dev up starts these panes:

Pane

Process

Address

backends

SearXNG and crawl4ai containers for agent web search

:8899, :11235

agents

the Seed Agents server with hot reload

:3051

web

the web app, talking to the desktop app's daemon

:3000

explore

the Hypermedia Explorer

:5173

notify

the notification server

:3060

vault

the vault

:3030

desktop

the Electron app on mainnet with its daemon

daemon HTTP :58001

hm-sync

the round trip between hypermedia/ and the desktop app


In mprocs, r restarts the focused process, s stops it, x starts it and q quits everything. Quitting also runs docker compose down for the search containers. The development daemon uses ports 58000 to 58004. The full port table is on Network.

Root package.json scripts start single apps: pnpm web, pnpm desktop, pnpm notify, pnpm explore, pnpm mobile, and pnpm web:standalone, which runs a web app against its own daemon.

Checks before a pull request

Run the checks for every area you touched. CI runs the same ones, and a single unformatted file anywhere fails the Lint job.

Area

Commands

Whole TypeScript workspace

pnpm typecheck

Formatting, everywhere

pnpm format:write, then pnpm format:check from the root; this covers the pnpm workspace, agents/ and vault/

Web, shared, desktop unit tests

pnpm test runs all three; singly pnpm web:test, pnpm shared:test, pnpm desktop:test:unit

SDK, UI, editor, notify, explore

pnpm --filter @seed-hypermedia/client test, pnpm --filter @shm/ui test, pnpm --filter @shm/editor test, and the same for @shm/notify and @shm/explore

CLI

pnpm --filter @seed-hypermedia/cli test (a fixture suite against a real daemon) and test:unit

Desktop end to end

pnpm desktop:test, which packages the app and drives it with Playwright

Integration

pnpm test:integration from the root

Mobile

pnpm mobile:test, pnpm mobile:typecheck

Agents

bun check and bun test in agents/; bun run protocol:check when you change the agents wire protocol

Vault

bun check and bun test in vault/

Dependencies

pnpm audit

Go

go test ./backend/... and golangci-lint run --new-from-merge-base origin/main ./backend/...

Docs

node scripts/hypermedia/check.mjs

In agents/ and vault/, bun check runs the type checker and rewrites formatting, so commit whatever it changes. Go tests need the submodule and model that direnv allow fetched. CI runs them with go test -tags cpu --count 1 ./backend/.... For backend bug fixes, add a failing test first when practical, and use testify/require in Go tests.

To reproduce CI locally before pushing, use agent-ci. docs/local-ci-with-agent-ci.md has the guide.

npx @redwoodjs/agent-ci run -w .github/workflows/test-frontend-parallel.yml -p --github-token npx @redwoodjs/agent-ci run -w .github/workflows/lint-go.yml -p npx @redwoodjs/agent-ci run -w .github/workflows/test-go.yml -p

Changes with special rules

Storage schema and migrations

The daemon's SQLite schema lives in backend/storage/schema.sql, which is the source of truth, and migrations live in backend/storage/storage_migrations.go. Read the comment at the top of that file before adding one. The rules it sets:

    A migration's version is a timestamp from date +%Y-%m-%d.%H%M%S, and the list is kept newest first.

    Its run function executes in an immediate write transaction and must be as idempotent as possible.

    A migration runs only when its version is higher than the data directory's, so never run a feature branch with a migration against a data directory you care about. Back it up first. Switching back to main afterwards fails on the unknown version.

    After changing the schema or migrations, run ./dev gen //backend/....

    Avoid migrations that force a full reindex or recompute embeddings unless you must. CPU-only servers take a long time to redo them. Prefer an additive column and a bounded background backfill.

When in doubt, ask the backend team before adding a migration. Tag the Go daemon maintainers on pull requests that touch backend/.

Protobuf and generated code

Edit .proto files under proto/, then run ./dev gen //proto/... from the root. Do not run buf or protoc directly, and do not format the generated code. Proto changes ripple into Go and TypeScript callers, so run the backend and frontend checks above. gRPC describes the services for API consumers.

Protocol changes

A protocol change is any change to the structure or meaning of permanent data (blobs, changes, the document graph, the CRDT rules), the sync protocol, the capability model, the identity system, or any format another implementation must read on disk or over the wire. A feature that can be built purely in an application is not a protocol change. The team's advice is to build it that way first.

The team's process for a protocol change, as written in its internal methodology notes:

    Write a proposal note describing the problem and the change.

    Map the arguments for and against it in the open, including rejected alternatives.

    Decide by working through the technical objections. Consensus does not decide.

    Update the protocol documentation, which is now this site.

The process is slow on purpose because published blobs are permanent. Every node that holds old data must keep reading it. Where this is going shows what this looks like in practice.

Deployment tooling

ops/ is built with the exact Bun version pinned in ops/package.json, and CI fails if ops/dist/deploy.js is stale. Rebuild it with that version, as bunx --bun bun@<version> build …, whenever you change the deploy script.

Releasing

Maintainers cut releases with the runbook in docs/releasing.md.

    Pick the version YYYY.M.N: the year, the month without zero padding, and the next number within the month. Check recent tags with git tag --sort=-creatordate | head -5.

    Tag the release commit, normally the tip of main, and push the tag. A *.*.* tag starts the Release - Desktop App and Release - Docker Images workflows.

    Wait for the desktop workflow to build every platform and create the GitHub release as a prerelease.

    Write short, user-facing release notes with Features and Bug Fixes sections and a full-changelog link, then publish them with gh release edit <tag> --notes-file <file> --prerelease=false --latest.

    Run the Generate latest.json (prod) workflow so desktop auto-update sees the new version.

Two packages publish on their own. Pushes to main that touch the SDK or the CLI run the publish-client workflow, so there is no manual npm step.

Contributing to these docs

This site is the hypermedia/ folder, and a commit to main publishes it through .github/workflows/sync-hypermedia.yml. Pages are plain markdown with a name and a one-sentence summary in the frontmatter. Links between pages are relative .md links. The dialect round-trips losslessly through the Seed app. hypermedia/README.md explains the layout, and Publish a folder explains the round trip.

node scripts/hypermedia/check.mjs # schemas, lockfile, generated types, bindings, frontmatter pnpm hypermedia:push -- --dry-run # what a publish would create, update, move or retire ./dev hm-sync # edit the folder in the desktop dev app and write edits back

Rules that matter when you edit:

    Nothing publishes while any relative link is broken, so fix links in the same commit that moves a page.

    Renaming a file in git publishes a move with a redirect at the old address.

    Deleting a file retires its document on the next push, unless the push runs with --keep-stale.

    A page with a *.schema.json beside it defines a schema. Keep its path, and let check.mjs confirm the lockfile and generated types.

    Keep the <!-- id:… --> comments on lines you keep. They are block ids. New pages need none.

Corrections are welcome as pull requests, or as comments on the published page.

Reporting security issues

Report security issues by email to security@hyper.media. Do not open a public issue or pull request about an unfixed vulnerability. This repository is public.

Once a vulnerability is fixed, the team discloses it as a GitHub issue closed by the fixing commit. The public record of what has been audited and which hypotheses were ruled out is docs/security/audit-log.md, and the audit procedure itself is docs/security/auditor.md. The known limits you should design around, such as the unauthenticated local daemon API and non-revocable capabilities, are listed on Integrity.

See also

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime