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.
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:
docker-compose.yml with the correct environment (keys, secrets, ports, network).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.
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.
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
The agent runs as a Docker container itself, with access to the host's Docker socket so it can manage sibling containers.
DOCKER_NETWORK (default: nodeyou):docker network create nodeyou
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.
| 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 |
When the agent provisions a profile, it generates a compose file at {COMPOSE_DIR}/profile-{subdomain}.yml. Each profile container gets:
{PROFILE_DATA_DIR}/{subdomain}.db{PROFILE_DATA_DIR}/{subdomain}_uploadsThe database and uploads directory are owned by UID 1000 (the profile container's application user).
/
├── 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
This project is licensed under the AGPL 3.0.