Underneath every Seed site and every Seed app is one process, the Seed daemon. It holds the blobs, indexes them, syncs with peers, and answers a gRPC API. The web server, the desktop app and the CLI's local development mode are all clients of that API.
Most builders never need it. The Seed API over HTTP and the SDK cover reading, searching and publishing, and they work against any site on the network without running anything. Use gRPC when you run a daemon yourself and want what only it can do: manage keys, subscribe to spaces, drive sync and discovery, list peers, or query the index in ways the HTTP keys do not expose.
Connecting
The daemon listens on three ports. Which numbers depends on who started it.
Listener | Daemon default | Seed desktop app | Dev build of the app | Site container |
|---|---|---|---|---|
HTTP, with gRPC-Web at |
|
|
|
|
plain gRPC |
|
|
|
|
libp2p |
|
|
|
|
Flags are -http.port, -grpc.port and -p2p.port. Every flag can also be an environment variable with the SEED_ prefix, so -http.port is SEED_HTTP_PORT. The full flag list is on the daemon page.
The plain gRPC port speaks standard gRPC with server reflection enabled, so grpcurl can explore it without the proto files. Against a running desktop app:
grpcurl -plaintext localhost:56002 listcom.seed.activity.v1alpha.ActivityFeed
com.seed.activity.v1alpha.Subscriptions
com.seed.daemon.v1alpha.Daemon
com.seed.documents.v3alpha.AccessControl
com.seed.documents.v3alpha.Comments
com.seed.documents.v3alpha.Documents
com.seed.documents.v3alpha.Resources
com.seed.entities.v1alpha.Entities
com.seed.networking.v1alpha.Networking
com.seed.p2p.v1alpha.P2P
com.seed.p2p.v1alpha.Syncing
com.seed.payments.v1alpha.Invoices
com.seed.payments.v1alpha.Wallets
com.seed.telemetry.v1alpha.Telemetry
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflectiongrpcurl -plaintext localhost:56002 com.seed.daemon.v1alpha.Daemon/GetInfo{
"state": "ACTIVE",
"peerId": "12D3KooWGZRLjYnJpZUQBFYu5qUqPK2rW2rA62GtdRSXGw6KvExz",
"startTime": "2026-09-16T18:15:28.665346Z",
"protocolId": "/hypermedia/0.9.2"
}grpcurl … describe com.seed.documents.v3alpha.Documents prints a service's RPCs and message types. The HTTP port carries the same services as gRPC-Web, which is what the browser-based clients use. The desktop app and the web server both connect with @connectrpc/connect transports to the HTTP port, never to the raw gRPC port. The daemon also embeds a grpcui at http://localhost:<http-port>/debug/grpcui/, reachable only from the same machine.
Message definitions live in the repository under proto/, one folder per service family, and generated clients are committed beside them: Go under backend/genproto and TypeScript under frontend/packages/shared/src/client/.generated. The TypeScript package exposes createGRPCClient(transport) with one property per service. After editing a proto file, ./dev gen regenerates both.
There is no authentication
The daemon's API has no authentication or authorization of its own. Anyone who can reach the gRPC or HTTP port can list keys, sign arbitrary bytes with any stored key, export keys, delete keys, store blobs, create refs and capabilities with any registered key, force a reindex, and read everything the daemon holds. Both ports bind all network interfaces, and the HTTP port answers any origin.
Write RPCs check the signing key. Every write takes a signing_key_name. The daemon loads that key from its own store and checks that it may write to the target space and path. It never asks whether the caller may write. Comments skip even that check: anyone may comment on anything.
The one piece of caller identity is the bearer token. Daemon.Authenticate proves possession of an account key and returns a token valid for thirty days. The HTTP middleware accepts it as Authorization: Bearer <token>. It has one use. When the daemon runs with -public-only, private content is hidden from anonymous requests and shown to authenticated callers who may write the space. Without that flag, the token changes nothing. The plain gRPC port ignores it entirely.
The protection comes from how you deploy the daemon. No setting adds it. A desktop app's daemon is reachable only on the local machine. A site's daemon publishes only its libp2p port outside the container network. Its HTTP and gRPC ports are reachable by the web server alone, and the reverse proxy forwards only /ipfs/* to it. If you run a daemon yourself, keep the two API ports behind a firewall or bound to a private interface. Integrity puts this in the larger picture of what the protocol does and does not verify.
Conventions
Addressing. A space is named by its account, the principal string of its key. A document is account plus path, where the empty path is the home document. A version is one or more Change ids joined by .. An empty version means latest. Comments and contacts are addressed as <author>/<tsid>. The proto files still say account where the rest of the documentation says space. A rename is on the backend team's list.
Pagination. Requests take page_size and page_token. Responses return next_page_token. Tokens are opaque cursors. A page size of zero or less means the handler's default, which differs by RPC between 10 and 100. Document listings cap larger requests at 2000. Some listings ignore paging and return everything. They are marked below.
Redirects. GetDocument on a path that holds a redirect Ref fails with FailedPrecondition and attaches RedirectErrorDetails {target_account, target_path, republish} to the status. GetDocumentInfo returns the redirect as data instead of failing. Follow redirects with a cycle guard.
Status codes.
Code | Meaning in this API |
|---|---|
| unparsable principal, id, version or page token; a missing required field |
| unknown key name, document, version or tracked domain |
| the path holds a redirect; a Ref with a different genesis and no higher generation; discovery disabled; vault not connected |
| the signing key may not write there; private content on a public-only node; a capability whose signer is not the account |
| a bearer token that is malformed, expired or unknown (HTTP |
|
|
| see the catalogue |
| storage failures and recovered panics |
Service catalogue
The daemon registers fourteen services, plus gRPC reflection. Each table lists every RPC with one line. Unimplemented RPCs still appear in the proto files and in reflection. Calling them returns Unimplemented.
Daemon
Node management, keys, authentication, the vault connection, and the domain tracker.
RPC | What it does |
|---|---|
| state ( |
| a fresh BIP-39 phrase, 12 words by default; stateless |
| derive a key from a mnemonic and store it under a name; the empty name becomes the public key string |
| load a |
| write a key file, optionally password-encrypted, to an absolute path on the daemon's filesystem |
| list, rename, delete; |
| sign arbitrary bytes with any stored key |
| store blobs atomically; a given |
| prove an account key and receive a thirty-day bearer token; the principal must already appear in some blob the daemon holds |
| rebuild every derived table from the stored blobs, synchronously |
| Unimplemented |
| the connection between this daemon and a remote vault that holds the user's keys; see identity |
| the tracker that polls |
Documents
The document model: reading, preparing changes, refs, accounts, profiles, contacts, listings and attribute queries. The proto file itself warns that this service is a kitchen sink.
RPC | What it does |
|---|---|
| a document at a version or latest; fails with redirect details on a redirect path |
| metadata, authors, breadcrumbs, activity summary, generation and redirect info, without the body |
| build an unsigned Change from edit operations against a base version; the client signs it and calls |
| Unimplemented; publish a tombstone Ref with |
| publish a version, tombstone or redirect Ref signed by a named key; timestamps round to milliseconds; a different genesis needs a higher generation. |
| accounts known to this node with their home document info, profile and alias |
| publish a Profile blob; the key must write the space root |
| link this key to another account; needs an AGENT capability |
| contact records; |
| children of a path, optionally recursive, with sort options |
| all documents, home documents, or documents whose parent does not link to them |
| filter by attributes, path, space and URL with and/or/not, sorted by built-in or user attributes; the query grammar |
| which attribute keys exist and which values a key takes, for autocomplete |
| the change history of a document and one change's author, deps and time |
| local read or unread state, never published |
AccessControl
RPC | What it does |
|---|---|
| capabilities that apply to a space and path; |
| capabilities granted to one principal |
| sign a capability delegating |
| one capability by id |
There is no RevokeCapability. Permissions explains what that means in practice.
Comments
RPC | What it does |
|---|---|
| publish a comment on a document version, optionally as a reply; visibility is inherited from the target at creation. Passing |
| by record id |
| every comment on a document; paging parameters are ignored |
| comments by one account |
| replace a comment with a full new snapshot |
| the signing key must be the author |
| reply count; every version of one comment, newest first |
Resources
RPC | What it does |
|---|---|
| resolve an |
| who links to, embeds or mentions a resource, in the order this node learned of them |
| announce documents to a specific peer and stream progress; what the desktop calls to publish to a site |
Entities
RPC | What it does |
|---|---|
| ask the network for a document, a subtree ( |
| full-text search over documents, comments and contacts with filters, plus semantic and hybrid modes when an embedding model is configured |
| deprecated; use |
| Unimplemented |
ActivityFeed and Subscriptions
RPC | What it does |
|---|---|
| new blobs and mentions this node has seen, filtered by author, event type or resource, ordered by claimed or observed time |
| subscribe to a space or path so the node keeps it synced; also triggers discovery and, unless |
| stop |
| everything subscribed; paging ignored |
Networking
RPC | What it does |
|---|---|
| a peer's addresses, connection status and account binding |
| known peers without their addresses; call |
| dial a multiaddr or a bare peer id now |
P2P and Syncing, the peer protocol
These are the RPCs peers exchange over libp2p. The network page explains the protocol. The daemon also re-exports them on its local gRPC server as a proxy: attach a target-peer metadata key holding a peer id, and the call is forwarded to that peer. This is how you inspect what another node offers.
RPC | What it does |
|---|---|
| stream the ids of every public blob, from a cursor |
| peer exchange and the spaces a node holds |
| prove an account key to a peer for this connection, which gives access to that account's private blobs |
| Lightning invoice; |
| range-based set reconciliation of blob sets per scope |
| the receiving end of a push |
Wallets and Invoices
Lightning wallets through a hosted lndhub at ln.seed.hyper.media (or the testnet host without -lndhub.mainnet): CreateWallet, ImportWallet, ExportWallet, RemoveWallet, ListWallets, GetWallet, GetWalletBalance, UpdateWalletName, GetDefaultWallet, SetDefaultWallet; CreateInvoice, PayInvoice, DecodeInvoice, ListPaidInvoices, ListReceivedInvoices. An LNURL service exists in the proto files but is never registered.
Telemetry
RecordCheckpoints appends client-side timing checkpoints to an in-memory buffer shown on the daemon's /debug/journeys page. The proto comment claims it is loopback-only. In fact it is registered on the public server like everything else.
The daemon's own HTTP routes
Besides gRPC-Web, the HTTP port serves a few plain routes. Debug pages answer only to the local machine and refuse browser requests from another origin, so curl works and an iframe does not.
Route | Purpose |
|---|---|
| bytes of a blob or file, with range support; searches the network for up to a minute when unknown |
| a structural blob decoded to DAG-JSON |
| multipart |
| store one raw block whose hash must match |
|
|
|
|
| completes a browser-mediated vault connection with a short-lived token |
| operator pages, local only |
Working with it
In the Seed app
The desktop app is a gRPC-Web client of its bundled daemon on port 56001. You can open http://localhost:56001/debug/grpcui/ in a browser on the same machine and call any RPC by hand.
CLI
The Seed CLI signs locally and talks to a site's Seed API. The one exception is seed-cli space dev, which registers its dev key in the desktop dev app's daemon over gRPC-Web (--daemon, default http://localhost:58001) and publishes through the app's HTTP bridge. For raw daemon calls use grpcurl as above.
SDK
@seed-hypermedia/client has no gRPC dependency by design, so it runs in browsers, Bun, Node and React Native. When you need the daemon, take the generated TypeScript clients from the shared package in the repository and a gRPC-Web transport pointed at the HTTP port. The desktop app and seed-cli space dev use @connectrpc/connect-web, and the web server uses @connectrpc/connect-node.
Agents
Seed Agents run beside a site and use the Seed API. They do not use gRPC. An external agent on a machine with a daemon can use grpcurl from its shell. It can fully control any daemon it can reach, which is one more reason to keep daemon ports off the network.
See also
Seed API, the HTTP surface built on top of these services
Daemon, flags, data directory and how the apps launch it
Network, what the P2P and Syncing services do between peers
Integrity, the trust model and its limits
Contributing, regenerating clients after a proto change
SDK, the client that needs no daemon
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime