The Seed daemon, seed-daemon, is the program that implements the Hypermedia protocol. It stores signed blobs, verifies and indexes them, keeps account keys, syncs with other nodes over libp2p, and serves everything to the apps through gRPC and HTTP. The desktop app spawns one for you. A site runs one in a container. The CLI and the agents service never need one, because they talk to a site's Seed API.
Where the code is
The daemon lives in backend/, written in Go. The entry point is backend/cmd/seed-daemon. The main packages are backend/blob (blob types, verification, the indexer), backend/api (the gRPC services), backend/hmnet (libp2p, sync, the file gateway), backend/storage (SQLite schema and migrations), backend/core (keys and signing) and backend/config (flags). The protobuf definitions are in proto/ and generate both the Go server and the TypeScript clients. Run ./dev gen after changing them.
Other binaries beside the daemon: monitord (health checks of sites), relayd (a libp2p relay), pingp2p (connectivity test), seed-sqlite and mkdb (database tooling).
What it listens on
Listener | Flag | Default | Serves |
|---|---|---|---|
libp2p |
| 55000 | peer-to-peer sync and the P2P RPCs |
HTTP |
| 55001 | gRPC-web for every service, |
gRPC |
| 55002 | the same services over plain gRPC |
Both gRPC and HTTP bind all interfaces, so other machines can reach them unless a firewall blocks them. The desktop app passes its own ports, listed on its page. A daemon you run yourself keeps the 55000 defaults. Every flag can also be set as an environment variable with the SEED_ prefix, so -p2p.port is SEED_P2P_PORT. SEED_DAEMON_FLAGS prepends extra flags.
Flags that matter to operators
Flag | Default | Meaning |
|---|---|---|
|
| Where everything is stored. The desktop app passes its own data directory. |
| false | Serve only public data in the APIs and over HTTP. Hosted sites and gateways use this mode. |
| empty | Use a file-based keystore in place of the OS keychain or vault. It is marked insecure. Sites use it inside their container. |
| empty | Joins a named testnet in place of mainnet by adding a suffix to the protocol id. |
| false | Disables circuit relay. |
| the Seed gateways and the public IPFS bootstrap peers | The peers a fresh node connects to first. |
| false | Refuse to fetch content the node does not have, turning off discovery. |
| false | Disable the sync scheduler, which also runs every |
| info | debug, info, warning or error. |
Network lists every timeout and constant. Self-hosting shows the flags a site container uses.
What it stores
The data directory holds three things.
keys/libp2p_id_ed25519 is the device key. It is separate from every account key.
vault.json is the encrypted local keystore for account keys. A secret in the OS keychain decrypts it.
db/db.sqlite is the SQLite database. It holds every blob, the index derived from them, full-text search, and the domain store.
The schema in backend/storage/schema.sql is the source of truth, and migrations only go forward. Reindexing rebuilds everything derived from the blobs and can take a long time on a large node. A debug flag forces a reindex with profiling.
The HTTP surface
Besides gRPC-web, the HTTP port serves the file gateway and a few utility routes.
GET /ipfs/<cid> returns a file or block. If the node does not have it, the daemon searches the network for up to a minute. GET /ipfs/<cid>.dagjson decodes a DAG-CBOR blob and pretty-prints it. It is the quickest way to inspect a change, ref or comment. POST /ipfs/file-upload chunks a file into UnixFS and returns its CID. POST /ipfs/<cid> stores one raw block. Both uploads accept up to 150 MiB and require no authentication. See Files.
GET /hm/api/config on the daemon returns its peer id, addresses and protocol id. The web app's version of the same route adds the registered account.
GET /debug/version reports the build. The other /debug/* pages answer only to loopback callers that send no cross-site fetch header. They cover metrics, pprof, the p2p and network reports, the SQLite pool and an embedded grpcui.
Authentication, plainly
The local gRPC and HTTP API has no authentication. Whoever can reach the port can read everything the node holds and can write as any key the node keeps, because signing happens inside the daemon. Bearer tokens exist, but they only widen reads on a public-only node. They never gate writes. The mitigations are deliberate and simple. Keep the ports on localhost or behind a firewall. Run any daemon that faces the internet with -public-only behind the web app. Sites are deployed this way. Integrity spells out what is verified and what is trusted.
Working with it
In the Seed app
The desktop app starts the daemon, waits for /debug/version, and then starts its own API bridge and the local agents server. Its settings show the daemon's peer id and addresses.
CLI
The CLI does not need a local daemon. seed-cli space dev talks to the desktop's API bridge and the daemon's HTTP port to publish a folder into the running app; see Publish a folder.
SDK
Web API
The web app answers every site's /api/<Key> routes by calling the daemon over gRPC-web. Over plain HTTP, the daemon itself serves only /ipfs and /hm/api/config. Building with gRPC lists the services for direct callers.
Agents
Seed Agents read and write through a site's Seed API and fetch media from the daemon's /ipfs route. In the desktop app they use the bundled daemon through the API bridge. Hosted agents use hyper.media.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime