← All docs · source: ADMIN-GUIDE.md

LazyToken Enterprise — Administrator Guide

Everything an administrator needs to install LazyToken, enroll a developer fleet, manage policy and access, apply a license, and read the dashboard. Commands and endpoints below are the ones the product actually implements; see server/openapi.yaml for the full API surface and docs/CONTRACT.md for the agent↔server wire protocol.

Conventions in this guide


1. Architecture in one paragraph #

LazyToken has three parts. The agent (ltk) runs on each developer machine, filters coding-agent terminal output before it reaches the model, and reports numbers only to your server. The server (Node/Fastify/PostgreSQL) ingests those metrics, signs and serves policy, enforces licensing softly, and runs the daily rollup and monthly ROI reports. The dashboard (React SPA, served by the same server) is where you read ROI, teams, opportunities, security events and fleet status. The server is self-hosted — you own the data; the vendor has no tenant and no access.


2. Installation #

Three supported paths, from simplest to strictest.

2.1 Docker Compose (MVP / pilots) #

From the repo (deploy/docker-compose.yml):

cd deploy
cp .env.example .env
# Edit .env — at minimum:
#   POSTGRES_PASSWORD=<strong>
#   JWT_SECRET=$(openssl rand -hex 32)
#   LICENSE_PUBLIC_KEY=<base64 ed25519 pubkey>   # optional until you install a license
docker compose up -d
curl http://localhost:8080/healthz    # {"status":"ok"}

The compose stack is PostgreSQL 16 + the server (which also serves the dashboard and runs the rollup job in-process). It publishes port 8080.

2.2 Kubernetes / Helm (GA) #

Chart at deploy/helm. Create secrets out-of-band, then install:

kubectl create namespace lazytoken
kubectl -n lazytoken create secret generic lazytoken-secrets \
  --from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
  --from-literal=SSO_ENCRYPTION_KEY="$(openssl rand -hex 32)"

helm install lazytoken ./deploy/helm -n lazytoken -f my-values.yaml
kubectl -n lazytoken rollout status deploy/lazytoken

The chart runs database migrations as a pre-install/pre-upgrade Helm hook Job before the server starts, ships an optional bundled PostgreSQL, an HPA, an Ingress, and a NetworkPolicy (networkPolicy.enabled=true) that technically enforces the zero-egress posture. Review deploy/helm/values.yaml for all knobs.

2.3 Air-gapped #

The strictest deployment — zero internet. Images are carried in as a tar, the license is a signed file (no license server to phone home), and a NetworkPolicy restricts server pods to DNS + in-cluster PostgreSQL. Full runbook: deploy/AIR-GAPPED.md.

The server makes no unsolicited outbound calls. The only optional egress paths are OIDC SSO (your IdP), and monthly-report delivery (SMTP / Slack) — both off by default, and only to endpoints you explicitly configure.

Prerequisites & caveats (read before production) #


3. First-admin bootstrap #

You need exactly one admin to start; everyone else is created from the dashboard.

Via environment (Docker/Helm): set BOOTSTRAP_ADMIN_EMAIL, BOOTSTRAP_ADMIN_PASSWORD, and BOOTSTRAP_ORG_NAME. On first boot, while no users exist, the server creates the org + admin. It is a no-op once any user exists.

Via CLI (local/dev):

cd server
npm run bootstrap:admin -- --email admin@acme.com --password '<strong>' --org "Acme"

Bootstrapping also creates the org's Ed25519 policy-signing keypair (the private key never leaves the server) and a signed default policy.

Log in to get a 12-hour session token:

curl -sX POST http://localhost:8080/v1/auth/login \
  -H 'content-type: application/json' \
  -d '{"email":"admin@acme.com","password":"<strong>"}'
# -> { "token": "<jwt>", "user": {...} }

Use that token as Authorization: Bearer <jwt> for the admin API. GET /v1/auth/me returns the current identity.


4. Licensing #

Licenses are offline, Ed25519-signed JSON bound to {org, seats, tier, expiry}. There is no license server and no activation call. Enforcement is soft: over-seat or expired produces warnings in GET /v1/license/status plus audit events — ingest and policy are never blocked. The one hard limit: new device enrollments fail with 403 SEAT_LIMIT when seats are exhausted; already-enrolled devices keep working.

Vendor side (issue a key):

cd server
npm run gen:license -- keygen                                   # one-time; keep the private key offline
npm run gen:license -- create --key <priv> --org "Acme" \
  --seats 50 --tier enterprise --expiry 2027-06-30
npm run gen:license -- verify --pub <pub> --license <key>

Customer side: configure LICENSE_PUBLIC_KEY (base64 raw 32-byte pubkey) on the server, then install the key once:

PUT /v1/admin/license   { "license_key": "<the signed key>" }

Check status any time: GET /v1/license/status.


5. Enrolling devices #

5.1 Create an enroll token #

Admin creates an org-scoped enrollment token (optionally with a max-uses limit and expiry):

POST /v1/admin/enroll-tokens   { "max_uses": 200, "expires_at": "2026-12-31T00:00:00Z" }
# -> { "token": "ORG-XXXXXXXXXXXX", ... }

List / revoke via GET /v1/admin/enroll-tokens and DELETE /v1/admin/enroll-tokens/{id}.

5.2 Enroll a machine #

On the developer machine (or via MDM):

ltk enroll --server https://lt.company.internal --token ORG-XXXXXXXXXXXX
ltk status        # enrollment, server, active policy, reporting state

ltk enroll stores the device JWT + refresh token + pinned policy public key in the OS keychain (service lazytoken) — never in files — and fetches the initial signed policy. From then on the agent reports metrics asynchronously and syncs policy on its own.

Useful agent commands:

CommandWhat it does
ltk enroll --server <url> --token <ORG-...>Enroll this device
ltk statusShow enrollment, active policy and reporting status
ltk policy syncFetch, verify (Ed25519) and apply the latest signed policy
ltk report flushCollect new metrics and send buffered batches now
ltk report statusShow pending records, cursor and backoff state
ltk update [--check]Update the agent from your server's channel — verifies sha256 + Ed25519 before an atomic self-replace
ltk sdlc syncFetch and apply signed org SDLC templates (CLAUDE.md/AGENTS.md standards)

Self-update pulls from your internal server only (GET /v1/agent/latest?channel=stable|beta, per CONTRACT §7): the download is verified against its published sha256 and an Ed25519 signature (the key pinned at enrollment) before an atomic replace that leaves the old binary intact on any failure — no root required. You can also roll out through the MDM/packaging channel (deploy/mdm/). Note: a dedicated open-source-licenses view (ltk licenses) is still a planned CLI addition (see 05-LEGAL-COMPLIANCE.md).

5.3 Fleet rollout via MDM #

Silent-install scaffolding lives under deploy/mdm/:

Package the agent with deploy/packaging/ (WiX MSI, pkg, deb/rpm), pre-seed the --server and --token values, and push through your MDM. Manage devices from the dashboard Fleet screen or GET /v1/admin/devices (revoke with DELETE /v1/admin/devices/{id}).


6. Teams, users and RBAC #

Four roles: admin, lead, viewer, finance.

Capabilityadminleadfinanceviewer
Read analytics dashboard✔ (team)
Download ROI PDF
Manage policies / assignments
Manage teams / users / devices
SSO / license / settings

Endpoints:

Every write here lands in the audit log (GET /v1/admin/audit-log), which is append-only.


7. SSO (OIDC / SAML) #

Configure per protocol; both are supported. Admin-only.

GET  /v1/admin/sso                 # list configured protocols (secrets masked)
PUT  /v1/admin/sso/oidc            # { enabled, config: { issuer, client_id, client_secret, ... } }
PUT  /v1/admin/sso/saml            # { enabled, config: { entryPoint, idpCert, ... } }
DELETE /v1/admin/sso/{protocol}

Login flows: GET /v1/auth/oidc…/oidc/callback, and GET /v1/auth/saml…/saml/acs. The OIDC client_secret is AES-256-GCM encrypted at rest using a key derived from SSO_ENCRYPTION_KEY (env only); secrets are never logged, echoed, or written to the audit log. SSO login state is DB-backed so it works across Helm replicas.

Caveat: the OIDC/SAML code paths are implemented and unit-tested, but each IdP (Azure AD, Okta, Google Workspace, ADFS) has its own claim/cert quirks. Validate the full login round-trip against your actual IdP in a staging org before enabling it for all users. MFA is delegated to your IdP.


8. Policies #

A policy is a signed JSON document the agent verifies before applying. Manage via:

GET/POST /v1/admin/policies
GET/PUT/DELETE /v1/admin/policies/{id}
POST/DELETE /v1/admin/policies/{id}/assignments        # assign to team or device

Each edit bumps the version and re-signs with the org's private key; the server serves the exact signed bytes and supports If-None-Match/304. Policy body (see CONTRACT §3) controls:

Unassigned devices get the org default policy. Assignments let you target a team (all its devices) or a single device. Because policies are signed, a compromised server cannot push a malicious policy — the agent rejects an invalid signature and keeps the last good one.


9. Settings (retention, token prices, report delivery) #

GET /v1/admin/settings
PUT /v1/admin/settings

Doc gap noted: PUT /v1/admin/settings accepts a reporting object (see server/src/routes/admin/org.ts), but the openapi.yaml request schema for that endpoint currently lists only retention_days and token_prices. The behavior is real; the spec is slightly behind.


10. Scheduled ROI reports #

The monthly ROI report summarizes savings and dollar value for a calendar month.


11. Reading the dashboard #

ScreenBacking endpointWhat it shows
ExecutiveGET /v1/analytics/summary, …/trendTokens saved, USD value, active/total devices, adoption %, 30/90-day trend
TeamsGET /v1/analytics/by-teamSavings and adoption per team; drill-down; anonymization mode
CommandsGET /v1/analytics/by-commandWhich tools save the most
OpportunitiesGET /v1/analytics/opportunitiesHigh-volume, low-savings passthrough tools to tune next
SecurityGET /v1/analytics/security-eventsContext Firewall events by type over time (type/tool/time only — never a value)
FleetGET /v1/admin/devicesEnrolled devices, silent/stale devices, agent versions
Policies / Settingsadmin endpoints abovePolicy editing, retention, prices, SSO, license

USD figures come from the token_prices table (§9). summary.adoption_pct is active devices (seen in the last 7 days) against seats.


12. Operations quick reference #

TaskHow
Health checkGET /healthz
Run the daily rollup manuallynpm run job:rollup
Run the monthly report manuallynpm run job:reports
Rate limitsIngest is 60 req/hour/device (INGEST_RATE_LIMIT_PER_HOUR)
Retention purgeRuns with the rollup; window = retention_days
Audit trailGET /v1/admin/audit-log (append-only)
BackupsBack up PostgreSQL; the org's policy private key lives in the orgs table — protect DB backups accordingly