node.you Logo Image node.you - Service Agent

The service agent is a lightweight FastAPI application that runs on each service server in a node.you deployment. It receives provisioning commands from the coordinator (the main node.you API) and manages the lifecycle of profile containers on its host machine.

How It Works

When a user subscribes to a profile service, the node.you API assigns their profile to a service server and sends a provisioning request to that server's agent. The agent then:

  1. Creates the profile's data directory and SQLite database file on the host.
  2. Generates a docker-compose.yml with the correct environment (keys, secrets, ports, network).
  3. Starts the profile container via docker compose up -d.

Teardown works in reverse: the agent stops and removes the container and its compose file, but preserves the data files for potential recovery.

Each profile runs as its own isolated Docker container on the service server, connected to the shared nodeyou Docker network so the reverse proxy can reach it.

Endpoints

All endpoints are prefixed with /api/internal/.

Endpoint Method Auth Description
/api/internal/provision POST Required Provisions a new profile container. Expects subdomain, port, and owner_public_key.
/api/internal/teardown POST Required Stops and removes a profile container. Expects subdomain. Data files are preserved.
/api/internal/purge POST Required Permanently deletes a profile's data files (db + uploads). Expects subdomain. Capacity accounting stays with the coordinator.
/api/internal/status POST None Returns the profile's status for the given subdomain.
/api/internal/health GET None Returns the agent's status and server name. Used by the coordinator to verify reachability.

Authentication is handled via the X-Internal-Token header, which must match the INTERNAL_SECRET environment variable. The health endpoint is exempt.

Configuration

The agent is configured entirely through environment variables:

Variable Description Default
INTERNAL_SECRET Shared secret for authenticating requests from the coordinator. Required; the agent refuses to start without it. (none)
SERVER_NAME Identifier for this service server, returned in health checks. unknown
DOMAIN The network's base domain for profile URLs. Baked into every provisioned container. Required. (none)
NODE_API_URL URL of the coordinator API, passed to profile containers so they can call back. Baked into every provisioned container. Required. (none)
PROFILE_API_IMAGE Docker image used for profile containers. nodeyou/profile-api:latest
DOCKER_NETWORK Name of the external Docker network profile containers join. nodeyou
PROFILE_DATA_DIR Path where profile databases and uploads are stored on the host. /var/lib/nodeyou/profiles
COMPOSE_DIR Path where generated docker-compose files are written. /var/lib/nodeyou/compose
PROFILE_BIND_ADDRESS Host address profile containers publish their port on. Bind to the WireGuard tunnel address on remote servers so containers are unreachable from the public internet; 127.0.0.1 on a coordinator-local agent. 0.0.0.0
HOST_PROFILE_DATA_DIR / HOST_COMPOSE_DIR Host-side paths written into generated compose files, when the agent container mounts them elsewhere. same as the container paths

The shipped docker-compose.yml derives both the agent's published address and PROFILE_BIND_ADDRESS from a single WG_ADDRESS value in .env (the WireGuard tunnel IP on remote servers, 127.0.0.1 on the coordinator). Note that .env values are compose-substitution inputs, not auto-injected container env: printenv WG_ADDRESS inside the container is empty by design.

Copy .env.example to .env and fill in your values:

cp .env.example .env

Deployment

The agent runs as a Docker container itself, with access to the host's Docker socket so it can manage sibling containers.

Prerequisites

  • Docker and Docker Compose installed on the host.
  • An external Docker network matching DOCKER_NETWORK (default: nodeyou):
docker network create nodeyou

Running

Fill in .env (including WG_ADDRESS), name the service and container in docker-compose.yml, then:

docker compose up -d --build

The compose file mounts the Docker socket and the host data directories into the agent container, and publishes the agent on WG_ADDRESS:8000 so the coordinator reaches it over the tunnel. On the coordinator itself, publish on 127.0.0.1:8001:8000 instead (the API owns port 8000 there).

When updating: --build applies code changes; --force-recreate alone only re-applies env/compose changes on the old image.

For the full deployment walkthrough (WireGuard, registering the server with the coordinator), see the self-hosting guides.

What Gets Mounted

Container Path Host Path Purpose
/var/run/docker.sock /var/run/docker.sock Allows the agent to run Docker commands on the host
/var/lib/nodeyou/profiles /var/lib/nodeyou/profiles Profile databases and upload directories
/var/lib/nodeyou/compose /var/lib/nodeyou/compose Generated docker-compose files for each profile

Generated Profile Containers

When the agent provisions a profile, it generates a compose file at {COMPOSE_DIR}/profile-{subdomain}.yml. Each profile container gets:

  • Its own SQLite database at {PROFILE_DATA_DIR}/{subdomain}.db
  • Its own uploads directory at {PROFILE_DATA_DIR}/{subdomain}_uploads
  • A unique JWT secret and anonymization salt (generated at provision time)
  • The owner's public key for signature verification
  • CORS origins scoped to the profile's subdomain
  • A port binding on the host for the reverse proxy to target

The database and uploads directory are owned by UID 1000 (the profile container's application user).

Project Structure

/
├── main.py                   # FastAPI application
├── Dockerfile                # Agent container image
├── docker-compose.yml        # Deployment compose file (WG_ADDRESS-driven)
├── requirements.txt          # Python dependencies
└── .env.example              # Environment variable template

License

This project is licensed under the AGPL 3.0.

Appearance
Theme
—