Policy Server Deployment and Operation¶
aigo-policy-server is the central serving spine for an organization's signed enterprise policy. It holds the organization's private Ed25519 signing key, serves the signed policy that every desktop and headless client verifies with only the public trust anchor, and handles device enrollment.
The policy server is server-side and private. It is a deliberately separate artifact from aigo-server (the inference server) and from the desktop app, and it is not bundled with the client. Keeping the signing key and the authored policies out of the artifact that ships to every user and inference node is least privilege: the signing key is the root of trust, so it lives only on the policy server you operate.
For the policy document schema (what a policy may target, lock, and gate), see the Policy Schema Reference. This page covers running the server.
What the policy server does¶
The server has one job split across four endpoints:
- It validates a one-time enrollment token and returns the public trust anchors plus polling metadata, so a new device can verify future policies.
- It serves the current signed policy.
- It serves a cheap policy id plus issue date for poll-without-download.
- It accepts signed client audit batches.
It does not run the inference runtime, the continuum-router, or any model loader. It reuses the shared aigo-policy crate for the policy schema and Ed25519 signing, and the same authenticated REST stack as the headless server.
Prerequisites¶
Before standing up the server, plan the following.
- A host reachable by your managed devices over HTTPS (terminate TLS at a reverse proxy in front of the server, or on the network path).
- A secure location for the private signing key, with access limited to the operators who manage policy.
- An authored
EnterprisePolicyJSON document (see the Policy Schema Reference).
Generate the signing key¶
The signing key is a PKCS#8 v2 Ed25519 key. Generate one and store it securely:
The key is printed as base64 to stdout (the human-readable notice goes to stderr, so the redirect captures only the key). This is the root of trust for your fleet. Store it in a secrets manager or an access-controlled file, back it up, and never commit it or place it on a client.
You may also keep the key as a raw binary PKCS#8 file. The server accepts either a raw PKCS#8 document or its base64 text encoding from --signing-key-file.
Extract and distribute the trust anchor¶
Clients verify the signed policy with the public trust anchor only. Print it from the signing key:
aigo-policy-server \
--signing-key-file policy-signing.pk8.b64 \
--anchor-id org-2026 \
--print-anchor
This prints a JSON trust anchor (an id and a base64 public key). The --anchor-id you choose is recorded in the served policy's anchorId and must match the anchor id you provision into clients.
You distribute this public anchor to devices inside the provisioning profile (trustAnchors). When a device enrolls against this server, the server returns the same anchor derived from this key, so a freshly enrolled device can verify the policy it then fetches. See Device Enrollment and Trust Anchors.
Author and sign the policy¶
Write your EnterprisePolicy document (for example /etc/aigo/policy.json) following the Policy Schema Reference. The server can sign it at startup, or serve an already-signed document verbatim.
Sign an authored policy at startup with the signing key:
aigo-policy-server \
--signing-key-file /etc/aigo/policy-signing.pk8 \
--policy-file /etc/aigo/policy.json \
--anchor-id org-2026
Serve a pre-signed SignedPolicy document verbatim (no signing key needed at runtime, useful when signing happens on a separate, more isolated host):
--policy-file and --signed-policy-file are mutually exclusive. The signing key may also be supplied through AIGO_POLICY_SIGNING_KEY (base64) or AIGO_POLICY_SIGNING_KEY_FILE. It is never read from the wire.
Validate the configuration without starting the server with --dry-run. It confirms the policy parses, the schema is in range, and the key signs, then exits.
Configure device enrollment¶
Enrollment is offered only when you configure one or more one-time tokens. Each token is single-use: it is consumed on the first successful enrollment and cannot be reused. A token can optionally bind the enrolling device to an organization label with TOKEN:ORG.
Pass tokens on the command line (repeat for several):
aigo-policy-server \
--signing-key-file /etc/aigo/policy-signing.pk8 \
--policy-file /etc/aigo/policy.json \
--anchor-id org-2026 \
--enrollment-token tok-alice:acme \
--enrollment-token tok-bob:acme \
--poll-interval-secs 900
Or supply a JSON file mapping tokens to organization labels (null binds without an org). Keep the file operator-only readable; tokens are never logged in clear:
aigo-policy-server \
--signing-key-file /etc/aigo/policy-signing.pk8 \
--policy-file /etc/aigo/policy.json \
--anchor-id org-2026 \
--enrollment-tokens-file /etc/aigo/enrollment-tokens.json
The poll interval you set (--poll-interval-secs, default 900) is returned to the device and controls how often it polls for an updated policy. When no enrollment tokens are configured, enrollment is simply not offered and POST /api/v1/enroll returns 503.
HTTP surface¶
The server binds to --host (default localhost, env AIGO_POLICY_HOST) and --port (default 8444, env AIGO_POLICY_PORT). The default port differs from the headless aigo-server default (8001) so a policy server and an inference server can share one box. Authentication is always required: the server overrides an auth-disabled config, because the policy and audit routes rely on device-signature verification before the enterprise_read scope check runs.
| Method | Route | Auth | Purpose |
|---|---|---|---|
POST | /api/v1/enroll | One-time enrollment token | Validates the token, returns trust anchors and poll interval |
GET | /api/v1/policy | enterprise_read | Returns the current SignedPolicy |
GET | /api/v1/policy/version | enterprise_read | Returns the policy id and issue date for cheap polling |
POST | /api/v1/audit | Enrolled device signature | Accepts signed client audit batches |
Device-signature auth grants the narrow enterprise_read scope to enrolled devices. API-key and session auth remain available for operator and admin flows. Client audit reporting is covered in Audit and Compliance.
Run as a service (systemd)¶
Run the server under a service supervisor so it restarts on failure and starts on boot. The unit below mirrors the hardening of the bundled aigo-server unit. Adjust paths, the user, and the listen address for your environment.
[Unit]
Description=Backend.AI GO Policy Server
Documentation=https://github.com/lablup/backend.ai-go
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=aigo
Group=aigo
Environment=AIGO_POLICY_SIGNING_KEY_FILE=/etc/aigo/policy-signing.pk8
ExecStart=/usr/bin/aigo-policy-server \
--host 0.0.0.0 \
--port 8444 \
--policy-file /etc/aigo/policy.json \
--anchor-id org-2026 \
--enrollment-tokens-file /etc/aigo/enrollment-tokens.json
ExecReload=/bin/kill -HUP $MAINPID
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadOnlyPaths=/etc/aigo
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Keep the signing key and the enrollment-tokens file readable only by the service user (chmod 600, owned by root:aigo or the service account). The ReadOnlyPaths=/etc/aigo line lets the service read its key and policy while preventing the process from modifying them.
Run in a container¶
The policy server runs cleanly in a container. Mount the signing key and policy as read-only, and publish the port behind your TLS terminator. Add a health check against the cheap version route:
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -fsS http://localhost:8444/api/v1/policy/version \
-H "Authorization: Bearer $AIGO_HEALTHCHECK_KEY" || exit 1
Inspect the resolved health state with docker inspect:
Verify the deployment¶
After starting the server, confirm it serves a policy and that the version route responds. The version route is the cheapest check and is what clients poll:
curl -fsS https://policy.acme.example/api/v1/policy/version \
-H "Authorization: Bearer <operator-api-key>"
A healthy response carries the policy id (the policyId from your document) and its issue date. Clients compare this id against their cached id and only download the full policy when it changes.
Command reference¶
| Flag | Env | Purpose |
|---|---|---|
--signing-key-file | AIGO_POLICY_SIGNING_KEY_FILE | PKCS#8 Ed25519 signing key (raw or base64). |
--anchor-id | AIGO_POLICY_ANCHOR_ID | Trust-anchor id recorded in anchorId and provisioned into clients. |
--policy-file | AIGO_POLICY_FILE | Authored EnterprisePolicy JSON, signed at startup. |
--signed-policy-file | AIGO_POLICY_SIGNED_FILE | Pre-signed SignedPolicy JSON, served verbatim. |
--enrollment-token | One-time token TOKEN[:ORG]. Repeatable, single-use. | |
--enrollment-tokens-file | AIGO_POLICY_ENROLLMENT_TOKENS_FILE | JSON map of token to org (null for no org). |
--poll-interval-secs | AIGO_POLICY_POLL_INTERVAL_SECS | Poll interval returned to enrolling devices (default 900). |
--host / -H | AIGO_POLICY_HOST | Bind host (default localhost). |
--port / -p | AIGO_POLICY_PORT | Bind port (default 8444). |
--data-dir / -D | AIGO_POLICY_DATA_DIR | Application data directory (access keys, settings). |
--config / -c | AIGO_POLICY_CONFIG | TOML config file (reuses the aigo-server config shape). |
--print-anchor | Print the public trust anchor and exit. | |
--generate-signing-key | Print a fresh base64 PKCS#8 Ed25519 key and exit. | |
--no-socket | Disable the Unix domain socket listener (TCP only). | |
--dry-run | Validate config, policy, and key, then exit. | |
--verbose / -v | Increase logging verbosity (-v, -vv). | |
--quiet / -q | Suppress non-error output. |
Next steps¶
- Choose how clients receive the policy in Deployment Models.
- Onboard devices in Device Enrollment and Trust Anchors.
- Follow the end-to-end setup and key-rotation procedures in the Administrator Guide.