Installation
Canonical reference for every way to install and run OGuardAI
Which path is right for you?
| Goal | Best path | Section |
|---|---|---|
| Quick eval (30 seconds) | docker run | 1. Quick Start |
| Local development | Docker Compose minimal | 2. Docker Compose |
| Full local with NER | Docker Compose full | 2. Docker Compose |
| Production VM / bare metal | systemd + reverse proxy | 7. Production Linux Server |
| Production containers | Docker Compose HA | 2. Docker Compose |
| Kubernetes | Helm chart | 3. Kubernetes |
| Build from source | Rust toolchain | 4. Local Build |
| Python SDK integration | pip install | 5. Package Managers |
| TypeScript SDK integration | npm install | 5. Package Managers |
| AWS deployment | ECS Fargate | 8. Cloud Deployments |
| GCP deployment | Cloud Run | 8. Cloud Deployments |
| Azure deployment | Container Apps | 8. Cloud Deployments |
1. Quick Start
docker run -p 3000:3000 \
-e GUARDAI_SESSION_SECRET=$(openssl rand -base64 32) \
ghcr.io/oronts/oronts-guardai/oguardai-server:latestcurl http://localhost:3000/v1/healthOne binary. Built-in regex detectors. Sealed sessions. No Python, no Redis.
2. Docker Compose
All compose files are in deploy/docker/. Run from the repository root.
Minimal (server only, built-in detectors)
docker compose -f deploy/docker/docker-compose.minimal.yml up --buildServer on port 3000. Regex detection covers: email, phone, IBAN, SSN, IP, URL, credit card, passport, DOB, address, customer/order ID, health ID.
Full Stack (server + NER detector)
export GUARDAI_SESSION_SECRET="$(openssl rand -base64 32)"
export GUARDAI_API_KEY_1="$(openssl rand -hex 32)"
docker compose -f deploy/docker/docker-compose.yml up --build| Service | Port | Description |
|---|---|---|
server | 3000 | Rust API server |
detector | 9090 | Python NER (spaCy/GLiNER) |
High Availability (2 instances + nginx)
export GUARDAI_SESSION_SECRET="$(openssl rand -base64 32)"
export GUARDAI_API_KEY_1="$(openssl rand -hex 32)"
docker compose -f deploy/docker/docker-compose.ha.yml up --buildTwo server instances behind nginx round-robin on port 8080. Uses sealed sessions (client-held encrypted blob), so no session state or sticky sessions are needed. Revocation is the one piece of shared state: both instances set GUARDAI_REVOCATION_BACKEND=redis and point at the bundled Redis (which stores only HMAC digests), so a value revoked on one instance is refused by the other.
Note: The sealed backend works well for HA because session state travels with each request, but any multi-instance deployment (sealed or redis sessions) must set
GUARDAI_REVOCATION_BACKEND=redisso a revocation on one instance is honored by all. For server-side sessions,session.backend=redisstores each session encrypted at rest in a shared Redis (cross-replica), and also requiresGUARDAI_REPLAY_BACKEND=redis, because the server rejects a redis session backend that has no redis replay backend.
3. Kubernetes (Helm)
Chart location: deploy/helm/oguardai/.
Basic Install
helm install oguardai deploy/helm/oguardai \
--set session.secret="$(openssl rand -base64 32)" \
--set auth.apiKeys[0].name=default \
--set auth.apiKeys[0].key="$(openssl rand -hex 32)" \
--set audit.backend=file \
--set audit.strict=trueThe chart defaults to auth.mode=api_key, and a non-dev server start requires a durable, strict
audit trail (audit.backend=file plus audit.strict=true), so both are set explicitly. The chart
mounts a volume at the audit path's parent directory; back it with a PersistentVolume for a trail
that survives pod restarts.
Key Values
server:
replicaCount: 1 # >1 (or autoscaling) requires revocation.backend=redis and idempotency.backend=redis, else the chart refuses to render
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { cpu: "1", memory: 512Mi }
session:
backend: sealed # sealed | memory | redis (managed is planned)
secret: "" # or use existingSecret / existingSecretKey
ttlSeconds: 3600
revocation:
backend: memory # memory | redis; MUST be redis when replicaCount > 1 or autoscaling, so a value revoked on one pod is refused by all
redis:
external:
url: "" # redis://host:6379; required when revocation.backend (or session.backend) is redis
auth:
mode: api_key # api_key | dev (jwt not supported by chart)
audit:
backend: log # log | file; non-dev needs file + strict: true
strict: false
file:
path: /audit/audit.log # emptyDir-backed volume unless you attach a PersistentVolume
detector:
enabled: false
auth: # X-Detector-API-Key shared secret; required when enabled
apiKey: "" # or existingSecret / existingSecretKey; allowInsecure: true is dev-only
existingSecret: ""
allowInsecure: false
resources:
requests: { cpu: 250m, memory: 512Mi }
limits: { cpu: "1", memory: 1Gi }
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
ingress:
enabled: false
className: ""
tls: []With Ingress and TLS
helm install oguardai deploy/helm/oguardai \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set ingress.hosts[0].host=oguardai.example.com \
--set ingress.hosts[0].paths[0].path=/ \
--set ingress.hosts[0].paths[0].pathType=Prefix \
--set ingress.tls[0].secretName=oguardai-tls \
--set ingress.tls[0].hosts[0]=oguardai.example.com \
--set ingress.annotations."cert-manager\.io/cluster-issuer"=letsencrypt-prodWith Redis for Distributed Sessions
Note: Set
session.backend=redisandredis.external.urlfor server-side sessions the client references bysession_id; each session is encrypted at rest (AES-256-GCM), so Redis holds only ciphertext. A Redis session backend advertises a multi-replica topology, so also setrevocation.backend=redis(the chart requires it, and a direct server start rejects redis sessions with a per-replica memory revocation table); the chart auto-enables the shared redis replay store for redis sessions. The sealed backend (default) also works for HA, carrying session state as a client-held encrypted blob with each request; run it across replicas withrevocation.backend=redistoo.
Namespace and RBAC
kubectl create namespace oguardai
# Create secrets (session key + API key)
kubectl create secret generic oguardai-secrets \
--namespace oguardai \
--from-literal=session-secret="$(openssl rand -base64 32)" \
--from-literal=api-key-default="$(openssl rand -hex 32)"
helm install oguardai deploy/helm/oguardai \
--namespace oguardai \
--set serviceAccount.create=true \
--set session.existingSecret=oguardai-secrets \
--set auth.existingSecret=oguardai-secrets \
--set auth.existingSecretKey=api-key-defaultThe chart supports ServiceAccount, PodDisruptionBudget (auto-enabled when replicas > 1), and NetworkPolicy.
4. Local Build (from source)
Prerequisites: Rust 1.88+ (required), Python 3.10+ (optional, NER), Node 20+ (optional, TS SDK), Tesseract OCR (optional, runtime; required only for the image routes /v1/transform/image and /v1/redact/image, which otherwise return 503 GUARDAI_OCR_UNAVAILABLE).
Server
cargo build --release -p oguardai-server
GUARDAI_SESSION_SECRET=$(openssl rand -base64 32) ./target/release/oguardai-serverListens on port 3000. Use --config oguardai.yaml for custom configuration.
CLI
cargo build --release -p oguardai-cli
./target/release/oguardai --helpoguardai transform --input "Contact julia@example.com"
oguardai detect --input "SSN: 123-45-6789"
oguardai run --config oguardai.yaml
oguardai config validate --config oguardai.yamlProxy
cargo build --release -p oguardai-proxy
./target/release/oguardai-proxy --target https://api.openai.com --port 8081Full Workspace
cargo build --release --workspace && cargo test --workspace5. Install from Package Managers
Rust CLI (when published):
cargo install oguardai-cliPython SDK:
pip install oguardai-sdkTypeScript SDK:
npm install @oguardai/sdk # or: pnpm add @oguardai/sdk6. Binary Download (GitHub Releases)
# Linux x86_64 (replace VERSION with the desired release tag, e.g. v0.1.0)
curl -fsSL https://github.com/oronts/oguardai/releases/latest/download/oguardai-${VERSION}-x86_64-unknown-linux-gnu.tar.gz \
| tar xz -C /usr/local/bin
# macOS x86_64
curl -fsSL https://github.com/oronts/oguardai/releases/latest/download/oguardai-${VERSION}-x86_64-apple-darwin.tar.gz \
| tar xz -C /usr/local/bin
# macOS ARM64 (Apple Silicon)
curl -fsSL https://github.com/oronts/oguardai/releases/latest/download/oguardai-${VERSION}-aarch64-apple-darwin.tar.gz \
| tar xz -C /usr/local/bin7. Production Linux Server (systemd)
Create /etc/systemd/system/oguardai-server.service:
[Unit]
Description=OGuardAI AI Data Protection Runtime
Documentation=https://github.com/oronts/oguardai
After=network.target
[Service]
Type=simple
User=guardai
Group=guardai
ExecStart=/usr/local/bin/oguardai-server --config /etc/guardai/oguardai.yaml
EnvironmentFile=/etc/guardai/oguardai.env
Restart=on-failure
RestartSec=5
TimeoutStartSec=30
TimeoutStopSec=30
# Security hardening
ProtectSystem=strict
ProtectHome=yes
NoNewPrivileges=yes
ReadWritePaths=/var/lib/guardai
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.targetGenerate secrets and create /etc/guardai/oguardai.env:
cat > /etc/guardai/oguardai.env <<EOF
GUARDAI_SESSION_SECRET=$(openssl rand -base64 32)
GUARDAI_AUTH_MODE=api_key
GUARDAI_API_KEY_1=$(openssl rand -hex 32)
# Non-dev (api_key) start requires a durable, strict audit trail; the unit's
# ReadWritePaths already covers /var/lib/guardai.
GUARDAI_AUDIT_BACKEND=file
GUARDAI_AUDIT_FILE_PATH=/var/lib/guardai/audit.log
GUARDAI_AUDIT_STRICT=true
RUST_LOG=guardai_server=info,tower_http=info
EOF
chmod 600 /etc/guardai/oguardai.envsudo useradd --system --no-create-home --shell /usr/sbin/nologin guardai
sudo mkdir -p /etc/guardai /var/lib/guardai
sudo chown guardai:guardai /var/lib/guardai
sudo systemctl daemon-reload && sudo systemctl enable --now oguardai-server
sudo journalctl -u oguardai-server -fReverse Proxy (nginx)
Put OGuardAI behind nginx with TLS termination:
server {
listen 443 ssl;
server_name guardai.yourcompany.com;
ssl_certificate /etc/ssl/guardai.crt;
ssl_certificate_key /etc/ssl/guardai.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE streaming support
proxy_buffering off;
proxy_cache off;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}sudo ln -s /etc/nginx/sites-available/guardai /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx8. Cloud Deployments
Audit trail: A non-dev (
api_key) start requires a durable, strict audit trail (GUARDAI_AUDIT_BACKEND=file+GUARDAI_AUDIT_FILE_PATHon a persistent path +GUARDAI_AUDIT_STRICT=true); the server refuses to boot otherwise. On platforms with ephemeral filesystems, either mount a persistent volume for the audit path or setGUARDAI_ALLOW_EPHEMERAL_AUDIT=1to explicitly accept an ephemeral trail (shown below).
AWS ECS (Fargate)
Image: ghcr.io/oronts/oronts-guardai/oguardai-server:latest, port 3000, health check /livez. Task config: FARGATE, 1 vCPU, 2 GB memory. Required env vars: GUARDAI_SESSION_SECRET (from Secrets Manager), GUARDAI_AUTH_MODE=api_key, GUARDAI_API_KEY_1 (from Secrets Manager), plus the audit trio above with GUARDAI_AUDIT_FILE_PATH on an EFS-backed volume (or GUARDAI_ALLOW_EPHEMERAL_AUDIT=1).
Google Cloud Run
# Create secrets first:
echo -n "$(openssl rand -base64 32)" | gcloud secrets create oguardai-session-secret --data-file=-
echo -n "$(openssl rand -hex 32)" | gcloud secrets create oguardai-api-key --data-file=-
gcloud run deploy guardai \
--image ghcr.io/oronts/oronts-guardai/oguardai-server:latest \
--port 3000 \
--set-env-vars GUARDAI_AUTH_MODE=api_key,GUARDAI_ALLOW_EPHEMERAL_AUDIT=1,GUARDAI_REVOCATION_BACKEND=redis,GUARDAI_HIGH_AVAILABILITY=true,GUARDAI_REDIS_URL=redis://your-redis-host:6379 \
--set-secrets GUARDAI_SESSION_SECRET=oguardai-session-secret:latest,GUARDAI_API_KEY_1=oguardai-api-key:latest \
--min-instances 1 --max-instances 10 \
--cpu 1 --memory 512Mi --region us-central1Cloud Run's filesystem is in-memory and ephemeral, so this example explicitly accepts an ephemeral
audit trail. For a durable one, mount a volume (e.g. Cloud Storage FUSE) and set the
GUARDAI_AUDIT_* trio instead.
Azure Container Apps
# Create secrets in Key Vault first, then:
az containerapp create \
--name guardai --resource-group guardai-rg \
--image ghcr.io/oronts/oronts-guardai/oguardai-server:latest \
--target-port 3000 --ingress external \
--min-replicas 1 --max-replicas 10 \
--cpu 1.0 --memory 2Gi \
--env-vars GUARDAI_AUTH_MODE=api_key GUARDAI_ALLOW_EPHEMERAL_AUDIT=1 GUARDAI_REVOCATION_BACKEND=redis GUARDAI_HIGH_AVAILABILITY=true GUARDAI_REDIS_URL=redis://your-redis-host:6379 \
--secrets session-secret=keyvaulturi,api-key=keyvaulturi \
--secret-env-vars GUARDAI_SESSION_SECRET=session-secret,GUARDAI_API_KEY_1=api-keyAs with Cloud Run, replica filesystems are ephemeral, so this example accepts an ephemeral audit
trail explicitly. Mount an Azure Files volume and set the GUARDAI_AUDIT_* trio for a durable one.
9. Python NER Detector (optional)
Adds NLP entity recognition (person names, companies, locations, medical terms) beyond built-in regex.
Docker (included in full compose):
docker compose -f deploy/docker/docker-compose.yml up detectorLocal (spaCy):
export GUARDAI_DETECTOR_API_KEY=$(openssl rand -hex 32) # same value on server and detector
cd apps/detector-py && pip install -e . && pip install -e ../../python/detector-core
python -m spacy download en_core_web_sm
uvicorn guardai_detector_service.main:app --host 0.0.0.0 --port 9090Local (GLiNER, recommended for multilingual):
export GUARDAI_DETECTOR_API_KEY=$(openssl rand -hex 32) # same value on server and detector
pip install oguardai-detector-core[gliner]
NER_BACKEND=gliner uvicorn guardai_detector_service.main:app --port 9090The detector handles raw PII, so its /detect endpoint fails closed (503) until
GUARDAI_DETECTOR_API_KEY is configured; set the same value on the server so it sends the
X-Detector-API-Key header. For local development only, GUARDAI_DETECTOR_ALLOW_INSECURE=true
runs the detector without a key.
Point the server to the detector, in oguardai.yaml:
detector:
advanced_url: http://localhost:9090Or: GUARDAI_DETECTOR_URL=http://localhost:9090, plus the matching GUARDAI_DETECTOR_API_KEY
10. Configuration Reference
oguardai.yaml
server:
host: "0.0.0.0"
port: 3000
session:
backend: sealed # sealed | memory | redis (managed is planned)
ttl_seconds: 3600
policy:
default: default
directory: /app/policies
auth:
mode: dev # dev | api_key | jwt | oidc
transform:
context_strategy: full
max_context_tokens: 4096
detector:
advanced_url: "" # Python NER URL (optional)Environment Variables
| Variable | Default | Description |
|---|---|---|
GUARDAI_SESSION_SECRET | (required) | Secret for AES-256-GCM session encryption (16 char minimum) |
GUARDAI_HOST | 0.0.0.0 | Server bind address |
GUARDAI_PORT | 3000 | Server listen port |
GUARDAI_AUTH_MODE | dev | Authentication mode: dev, api_key, jwt, oidc |
GUARDAI_API_KEY_<NAME> | (none) | Registers an API key (auto-upgrades dev to api_key) |
GUARDAI_DETECTOR_URL | (none) | Python NER service URL |
GUARDAI_DETECTOR_API_KEY | (none) | Shared secret for the server-to-detector hop (X-Detector-API-Key); set on both sides |
GUARDAI_REDIS_URL | (none) | Redis URL for the shared revocation, replay, and session backends |
GUARDAI_AUDIT_BACKEND | log | Audit sink: log (ephemeral) or file (durable, hash-chained). Non-dev requires file + strict |
GUARDAI_AUDIT_FILE_PATH | (none) | Append-only audit log path (required with the file backend) |
GUARDAI_AUDIT_STRICT | false | Fail a request closed (503) when its audit event cannot be recorded |
GUARDAI_ALLOW_EPHEMERAL_AUDIT | (unset) | Waive the non-dev requirement for a durable, strict audit trail |
RUST_LOG | guardai_server=info | Log level filter |
NER_BACKEND | gliner | Python detector engine: gliner, spacy, none |
GLINER_MODEL | urchade/gliner_medium-v2.1 | GLiNER HuggingFace model name |
11. Verification
After any install method:
# Liveness probe (unauthenticated)
curl http://localhost:3000/livez
# Health check (requires auth when enabled)
curl -H "X-API-Key: $GUARDAI_API_KEY" http://localhost:3000/v1/health
# List capabilities
curl -H "X-API-Key: $GUARDAI_API_KEY" http://localhost:3000/v1/capabilitiesRound-trip test:
# Transform
RESPONSE=$(curl -s -X POST http://localhost:3000/v1/transform \
-H "Content-Type: application/json" \
-H "X-API-Key: $GUARDAI_API_KEY" \
-d '{"input": "Contact Julia at julia@example.com or call 555-0123."}')
echo "$RESPONSE" | jq .
# Rehydrate
SESSION_STATE=$(echo "$RESPONSE" | jq -r '.session_state')
curl -s -X POST http://localhost:3000/v1/rehydrate \
-H "Content-Type: application/json" \
-H "X-API-Key: $GUARDAI_API_KEY" \
-d "{\"output\": $(echo "$RESPONSE" | jq '.safe_text'), \"session_state\": \"$SESSION_STATE\"}" \
| jq .Port note: The server default port is 3000 (
server.port/GUARDAI_PORT), and every install method on this page publishes it as host port 3000, except the HA compose stack, which fronts the two instances with nginx on host port 8080. If port 3000 is taken on your host, map another one (for example-p 8080:3000) and substitute it in the examples.