Skip to content

Latest commit

 

History

History
146 lines (110 loc) · 7.03 KB

File metadata and controls

146 lines (110 loc) · 7.03 KB

Services: the platform

Ensemble is a multi-service platform, not a single app. The daemon is the substrate; services are the inhabitants. A chat bot, a game matchmaker, a group-chat host, and the package registry itself are all just services.

This doc is the map. To build one, follow creating-a-service.md. The trust model underneath is security.md (and ADR-0002).

What a service is

A service is any program that registers with a daemon via the RegisterService API. In return it gets:

  • Its own cryptographic identity — an E… address derived deterministically from the daemon's master seed + the service name. Same name → same address, stable across restarts and upgrades, for free.
  • Its own connection-authorization policy — a manifest ACL, default-deny, with opt-in escalations (dial-as-self, decides_connections).
  • Its own message stream — chat envelopes or raw RPC bytes, plus connection requests, file offers, command invocations, and peer introductions.

A service can run three ways, all over the same SDK surface:

Shape How it connects Auth
Standalone / sidecar dials a socket or TCP addr admin seed signs every RPC
Supervised install daemon execs it with a spawn contract per-spawn token
Launched app daemon launches a separate GUI process and brokers its link scoped launch grant

Client.from_env() (both SDKs) picks the right one from the environment, so the same build runs supervised or standalone unchanged.

Built-in services

The daemon ships two services itself:

  • node — chat, file transfer, and contacts: the core peer-to-peer messaging surface.
  • ui — the embedded Solid.js web client, served over its own onion (and optionally local HTTPS / LAN HTTP). See running.md → Web UI.

SDKs

Write services in Python or .NET; the daemon doesn't care what language a service is in — it speaks gRPC over a socket.

  • Pythonpip install ensemble-client. Async API, dataclass events, Client.from_env() for supervised installs. ../clients/python/README.md.
  • .NET 8dotnet add package Ensemble.Client. Typed records, IAsyncDisposable lifetimes, typed exceptions. ../clients/dotnet/README.md.

Both expose the same three transports — chat (envelopes), RPC (raw protobuf bytes), and the introduction primitive for wiring two strangers together with a daemon-attested provenance stamp.

Distribution: registries

A registry is itself a service. It serves a signed, hash-pinning index; subscribing pins its E… address, and that pin is the whole trust decision. Install resolves a name through your subscribed registries, hash-verifies the artifact, then the daemon's supervisor runs it. The full packaging → publish → install → supervise story is ADR-0009 and the service playbook.


Showcase: what people have built on it

These are separate projects that live on the platform — the proof that the "platform, not app" framing is real rather than aspirational. Each one leaned on a different platform primitive, which is the interesting part.

PUG — matchmaking for multiplayer games

Pick-Up Group: a .NET matchmaking library — queues, matchers, private lobbies, and sessions — that runs on the Ensemble P2P network. The matchmaker is a public, RPC-transport service: it pairs players and introduces them to each other, but hands out no grants. Each player runs a decides_connections service that accepts a dial iff it came from a peer the verified matchmaker just introduced — then the two players connect directly (player-service to player-service) over QUIC and the matchmaker drops out of the data path entirely.

This is the canonical worked example of the ADR-0002 model: introductions are information, the game owns its own policy, and the core stays generic and default-deny.

Primitives exercised: RPC transport, IntroducePeers, decides_connections, direct peer-to-peer QUIC after introduction.

Jeff — an AI companion

A single-user AI companion (an Ollama-backed LLM with pgvector long-term memory) that lives as a daemon-supervised package inside an Ensemble pod. Jeff is a chat-transport service with an operator-only allowlist — the contact list is the operator, never the public — and an "inner life" / proactive loop that lets it initiate contact rather than only replying.

Jeff is the showcase for the supervised-install lifecycle: it's installed and run by the daemon's supervisor (readiness = a clean RegisterService, liveness = the register stream, restart = exponential backoff), its identity and data dir survive upgrades for free, and its secrets — the system prompt and model config — arrive through the per-service env overlay rather than being baked into the (public) artifact. Built in Python on the Python SDK.

Primitives exercised: supervised install + spawn contract, env overlay for secrets, stable derived identity across upgrades, operator-only ACL.

gomp — decentralized group rooms

G.O.M.P. ("Get Outta My Pub") — a small, self-hostable, decentralized take on a Discord server. It's a launched app (ADR-0010 / ADR-0011): a host service (the room) plus a separate desktop client process that the daemon launches and whose link it brokers, relayed through the daemon. Rooms are host-led (ADR-0007) — a room is just a service some node hosts, and the pub metaphor runs through the UI (getting kicked means "you're barred").

gomp is the showcase for services bringing their own frontend: rather than a declarative markdown page, it launches its own GUI process (any stack) and the daemon's launcher fires it up and brokers the client↔host relay datapath. It lives in its own repo and installs through the same registry + supervisor path as everything else.

Primitives exercised: launched-app lifecycle (ADR-0011), host-led group rooms (ADR-0007), services-bring-their-frontend (ADR-0010), client↔host relay through the daemon.


Build your own

The end-to-end guide — SDK contract, manifest/capabilities, packaging, the spawn contract, the env overlay, registries, and install/management — is the service creation playbook. The relevant ADRs: