Enterprise Edition
A design for a web-only Seed instance on one dedicated VPS that is fully locked down for private use, built from the pieces Seed already ships (site stack, vault, notify, agents) plus the isolation and access-control changes the product still needs.

Enterprise Edition

Status: design proposal, 2026-10-11. Written against the code and the published docs as of today; every "today" claim links to the page that describes it, and every "proposed" item is a change Seed does not have yet.

What it is

A single-tenant, web-only Seed for one organization. One dedicated VPS runs the whole product: the Seed daemon, the web app, the identity vault, the notify service and, optionally, the Seed Agents server. Members use it only in a browser. The box does not talk to the public Hypermedia network: it does not dial the public gateways, nothing it stores is reachable from outside, and no content leaves it except email the organization chose to send.

The point is not a different protocol. It is the same signed, content-addressed documents, the same accounts and capabilities, the same web app. What changes is the boundary: today a Seed site is a node on one shared network that also has a web front; the Enterprise Edition is a Seed node with no network behind it, where the web front is the only door and sign-in is the lock on that door.

Three promises define it:

    Nothing leaves the box. No peer connection in or out, no discovery, no bootstrap, no relays, no telemetry. Backups and email are the only outbound flows, and the operator controls both.

    Nobody reads without signing in. Every page, every API call and every file by CID requires a session belonging to a member. There is no anonymous surface.

    Keys stay with the people. Members hold their own account keys in the on-box vault; the browser signs with delegated session keys; the server never holds a member's key in the clear. The organization's space key is the one key the box holds, and it can live in a hardware token or an operator-held file.

What Seed already has

The design reuses six things that exist today.

Piece

What it does today

Reference

Site stack

seed-deploy runs Caddy, the web app and the daemon as three containers under /opt/seed, gets TLS itself, backs up in one command

Self-hosting

-public-only daemon

Hides private blobs from HTTP unless the request carries a bearer token minted for a space owner or a holder of a root WRITER or AGENT capability; the web app forwards the visitor's cookie as that token on every daemon call

Privacy, Web app

Per-blob visibility

A Ref or comment can be Private; the daemon tracks which spaces may see each blob and filters peer sync so an unauthorized peer never learns hidden blobs exist

Privacy

Capabilities

WRITER (scoped to a path) and AGENT (full delegation) grants signed by the space owner, checked by every node when it indexes

Permissions

Vault + Sign in with Seed

Account keys encrypted in the browser, stored as ciphertext; websites act for an account through a browser session key and a vault-signed capability; the vault has a WebAuthn relying party per origin

Vault, Sign in

Isolation flags

-p2p.testnet-name changes the libp2p protocol id, -p2p.bootstrap-peers replaces the compiled-in gateway list, -syncing.no-discovery and -syncing.no-pull stop the node from fetching anything, -p2p.no-relay disables circuit relay

Daemon, Network

Two facts about today's code shape the plan. First, the daemon's local API has no authentication: whoever reaches its ports reads everything and signs as any key it keeps (Daemon). So the daemon must never be reachable except from the web app's container network, which is already how seed-deploy lays it out. Second, private document creation is switched off in the daemon's PrepareChange since September 2026, and nothing is encrypted at rest (Privacy). The first release therefore does not depend on per-blob visibility at all; it gets privacy from the box being unreachable, and adds visibility in a later phase.

The box

One VPS, one docker compose project, one directory. Sizing: 4 vCPU, 8 GB RAM, 100 GB NVMe covers a few hundred members with documents, images and comments; agents with the execute sandbox need a VPS with nested virtualization (KVM), which most providers sell as "dedicated" or "metal" instances.

flowchart LR subgraph internet [Members] B[Browser] end subgraph vps [Dedicated VPS] C[Caddy :443] W[seed-web] V[seed-vault] N[seed-notify] A[seed-agents optional] D[seed-daemon] S[(blobs + SQLite<br/>encrypted volume)] C --> W C --> V C --> N C --> A W --> D V --> D N --> D A --> W D --> S end B -- HTTPS only --> C N -. SMTP, allowlisted .-> M[Mail relay] A -. LLM API, allowlisted or none .-> L[Model provider]

Container

Image

Reachable from

Role

seed-proxy

caddy:2

the internet on 443 only (80 redirects or is closed)

TLS, routing, the sign-in gate for static paths

seed-web

seedhypermedia/web

proxy only

pages, the Seed API, site services, browser signing

seed-vault

seedhypermedia/vault

proxy only, at /vault/

member accounts, passkeys, delegation, "Identity"

seed-notify

seedhypermedia/notify

proxy only, at /notify/

inbox and email notifications

seed-agents

seedhypermedia/agents

proxy only, at /agents/

optional; Seed Agents against this site

seed-daemon

seedhypermedia/site

container network only

storage, indexing; no published port

The daemon's libp2p port (56000 on a normal site) is not published at all. Its HTTP and gRPC ports stay on the container network, as today. Everything under /opt/seed sits on a LUKS-encrypted volume (or the provider's encrypted block storage), because blobs and the SQLite database are stored in the clear by design.

Host firewall: inbound 443 (and 22 from the operator's addresses); outbound denied by default, with allow rules only for the mail relay, the model provider endpoints the organization chose (or none, with a local model), the Docker registry during updates, and the backup target.

Web-only means no desktop app

A desktop app is a full node: it syncs peer-to-peer and keeps its own copy of every space it follows. Letting one into the organization's network would make it a second box to lock down. The Enterprise Edition supports browsers only. The web app already does everything a member needs: reading, editing, commenting, the Explore query grammar, the inbox, and signing in the browser with WebCrypto keys (Web app). If a desktop later becomes necessary, it joins the same way a second server would: over a VPN, with the same testnet name, authenticated by a capability.

Layer 1: network isolation

The daemon is started so that it cannot find or be found by anyone.

Setting

Value

Effect

-p2p.bootstrap-peers

empty

never dials hyper.media, dev.hyper.media, staging.hyper.media or the community node

-p2p.testnet-name

ent-<org>

announces /hypermedia/0.9.2-ent-<org>, so even an accidental connection from a mainnet peer fails the protocol handshake

-p2p.no-relay

true

never asks the public relay servers for a reservation

-syncing.no-discovery

true

DiscoverEntity never fetches content the node does not have

-syncing.no-pull

true

the sync scheduler is off; subscriptions never rerun

-p2p.port

unpublished

the listener exists inside the container but no host port maps to it

With this, the usual leak paths are closed: the bootstrap dial at startup, peer exchange, relay reservations, the AutoNAT probes, discovery runs triggered by a page load, and the 60-second peer-table tick (Network). Every one of these is driven by the compiled-in bootstrap list or by discovery, and both are off.

Two more things are only true of the network and should be stated. The home document's siteUrl still gets set at registration (Sites); on an isolated node it points at the private hostname and nobody outside can fetch /hm/api/config anyway. And the Explore and search features run entirely against the local index, so they keep working.

Proposed change: a daemon flag -p2p.disabled that skips starting the libp2p host entirely, instead of leaning on five flags staying consistent. It is the cheapest way to make "no network" a single line in a config file and a single line in doctor output.

Layer 2: identity

The vault runs on the box at https://<org-host>/vault/, with SEED_VAULT_RP_ID set to the organization's hostname so passkeys are bound to it (Vault). Each member creates an account there, protected by a passkey or a password; the server stores ciphertext only. Signing in to the site uses the existing ceremony: the web app makes a session key in the browser, the vault signs a capability for it, and from then on the browser signs as the member (Sign in). Nothing about this is new.

The organization has one space: an account whose key the operator creates at install time. That key is the only account key the box holds. Options, in order of preference: a hardware token with the signing done through the vault's connect flow; a file keystore on the encrypted volume (-keystore-dir, the same mechanism sites use today); or a key the operator keeps offline and uses only to issue capabilities.

Members get capabilities on the organization's space. Today the roles are WRITER (path-scoped) and AGENT (full delegation) (Permissions). For an organization that maps naturally: a member is a WRITER on the paths of their team (/engineering/*, /finance/*), admins are AGENTs, and read access is universal because every member is signed in and the content is only on this box. Capabilities are ordinary signed blobs, so the admin UI for "add a member" is: create a vault invitation, and on acceptance sign a capability from the space key to the member's account.

Proposed changes:

    Invite-only vault. Today anyone who reaches the vault can create an account. Add SEED_VAULT_INVITE_ONLY=1 and an admin endpoint that mints single-use invitation links tied to an email; the registration page refuses without one.

    Members directory. A small admin page on the web app listing accounts, their capabilities and last sign-in, backed by the vault's account table and the daemon's capability index. Removing a member revokes their vault session keys and publishes a tombstone for their capability.

    SSO. Optional OIDC in front of the vault (Okta, Entra, Google Workspace): the vault accepts an ID token as proof of email for invitation acceptance and as the second factor for sign-in. The account key still lives in the vault, never with the identity provider.

    A READER role. The protocol has only WRITER and AGENT. A reader today is simply "signed in on an isolated box". When the box is not the whole boundary any more (phase 2 below), reads need a role of their own; the permissions page already records that a path-scoped WRITER is denied private reads, which is the same gap from the other side.

Layer 3: the door

Everything the proxy serves requires a member session. Today a Seed site is public by default: pages, /api/*, /ipfs/* and .md exports answer to anyone, and sign-in only widens what a visitor may see (Web app). The Enterprise Edition inverts the default.

Proposed change: SEED_REQUIRE_SIGN_IN=1 on the web app. Every route except the sign-in pages, the vault callback and /hm/api/config answers 401 without a valid session cookie. The session is the same browser session key the sign-in ceremony creates, so no second authentication system appears. The daemon stays -public-only, and the web app keeps forwarding the session as the bearer token, which is exactly what makes private content readable to members today.

Caddy adds what the application should not have to: rate limits, a request-size cap, a second gate on /ipfs/* (a signed, short-lived cookie the web app sets at sign-in, so a leaked CID link does not fetch the file), and optionally mutual TLS or a VPN-only listener for organizations that want no public IP at all.

Notifications keep working: the notify service reads the daemon's activity feed and sends email through the organization's own relay, with NOTIFY_SMTP_* pointing at it and NOTIFY_TRUSTED_PREVALIDATORS set to the on-box vault (Notify). Email links point at the private hostname and require sign-in like every other page.

Agents, optionally

The Seed Agents server already self-hosts as seedhypermedia/agents with --hm-server-url pointing at the site it reads and writes through (Agents). On the box it runs as one more container, so agents read exactly what members read and publish with their own signing identities under capabilities the organization granted. Two operator choices decide how private it is:

    Model provider. Cloud providers mean document text leaves the box in prompts; the egress allowlist names exactly which endpoints. A local model through the ollama provider type keeps everything on the VPS, at the cost of a GPU instance.

    Code execution. The execute tool needs the microVM sandbox, which needs KVM on the host. Without it the tool is simply absent (Tools). The warm pool and the host-wide concurrency cap are the knobs to size for a shared box.

Web search and web reading are off by default in this edition, since both would open egress; an organization can allow SearXNG and the crawler explicitly.

Operations

Concern

How

Install

seed-deploy --profile enterprise: the wizard asks for hostname, SMTP, the space key source and whether agents run; it writes the compose file with every isolation flag and the vault, notify and agents containers. Proposed; today the wizard knows only the three-container site.

TLS

Let's Encrypt when the hostname is public; otherwise the organization's CA through Caddy's tls directive, or a VPN-only listener

Updates

pinned image tags, not the ten-minute cron. seed-deploy deploy --tag 2026.10.x after the organization's own review; doctor reports drift

Backups

seed-deploy backup already produces one archive of config, web, daemon and proxy state; add vault and notify data and encrypt the archive with an operator key before it leaves the box

Restore

seed-deploy restore <file> on a fresh VPS, then re-point DNS; the space key is in the archive only if the operator chose the file keystore

Monitoring

the existing metrics profile (Prometheus, Grafana) served under /.metrics, behind the same sign-in gate

Audit

proposed: an append-only log of sign-ins, capability grants and revocations, admin actions and exports, written by the web app and vault to a table the operator can ship to their SIEM

Version

/hm/api/version reports the web app and daemon commit, as today

What the operator never has to do

Run a key ceremony for members (the vault does it), manage a P2P port or NAT (there is none), curate a bootstrap list, or reason about which documents are public, since in phase 1 nothing is reachable from outside at all.

Phases

Phase 1, isolation. Everything above that is marked "today" plus three changes: the enterprise deploy profile, SEED_REQUIRE_SIGN_IN, and the invite-only vault. Privacy comes from the box boundary. Documents are created as ordinary public documents inside a node nobody else can reach. This is enough for a pilot and is mostly packaging.

Phase 2, visibility. Re-enable private document creation behind a server flag (-allow-private-create), fix the two gaps the privacy page records (sub-path WRITERs denied private reads; CreateRef publishing public Refs), and add the READER role. Now the box can be opened a crack: a second site in another region, or a desktop node for one power user, without the whole space becoming visible to whatever peer connects. This is the phase that touches the daemon's indexer and needs the most care.

Phase 3, federation. Two Enterprise Editions, or an Enterprise Edition and the public network, exchanging a chosen subset of spaces over an authenticated link: the same testnet name on both, an allowlist of peer ids, and capabilities as the only authority. This is where the organization publishes its public documentation to hyper.media from the same box it writes private work on, and it is exactly the direction the protocol already points in: visibility on the Ref, authority in the capability, and the network as a dumb transport.

Open questions

    Licensing and support. Whether this ships as a paid profile of the open-source deploy script, a separate image channel, or a hosted "dedicated" tier where Seed operates the VPS for the customer. The technical design is the same in all three.

    Space-per-team or one space. One organization space with path-scoped WRITERs is simplest and matches today's roles. Separate spaces per team give stronger isolation but need cross-space linking and a READER role to be comfortable.

    Key recovery. The vault's zero-knowledge design means a member who loses passkey and password loses the account. Enterprises will want an admin recovery path, which must be designed so the operator still cannot read keys in normal operation (for example, a recovery share held by the organization and a second by the vault, combined only in a logged recovery ceremony).

    Where the first customer is. The design is cheap in phase 1; the question is whether a pilot organization exists to run it against real documents before phase 2 is built.

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

Unsubscribe anytime