OGuardAI
Getting Started

Installation

Canonical reference for every way to install and run OGuardAI

Which path is right for you?

GoalBest pathSection
Quick eval (30 seconds)docker run1. Quick Start
Local developmentDocker Compose minimal2. Docker Compose
Full local with NERDocker Compose full2. Docker Compose
Production VM / bare metalsystemd + reverse proxy7. Production Linux Server
Production containersDocker Compose HA2. Docker Compose
KubernetesHelm chart3. Kubernetes
Build from sourceRust toolchain4. Local Build
Python SDK integrationpip install5. Package Managers
TypeScript SDK integrationnpm install5. Package Managers
AWS deploymentECS Fargate8. Cloud Deployments
GCP deploymentCloud Run8. Cloud Deployments
Azure deploymentContainer Apps8. 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:latest
curl http://localhost:3000/v1/health

One 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 --build

Server 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
ServicePortDescription
server3000Rust API server
detector9090Python 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 --build

Two 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=redis so a revocation on one instance is honored by all. For server-side sessions, session.backend=redis stores each session encrypted at rest in a shared Redis (cross-replica), and also requires GUARDAI_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=true

The 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-prod

With Redis for Distributed Sessions

Note: Set session.backend=redis and redis.external.url for server-side sessions the client references by session_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 set revocation.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 with revocation.backend=redis too.

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-default

The 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-server

Listens on port 3000. Use --config oguardai.yaml for custom configuration.

CLI

cargo build --release -p oguardai-cli
./target/release/oguardai --help
oguardai transform --input "Contact julia@example.com"
oguardai detect --input "SSN: 123-45-6789"
oguardai run --config oguardai.yaml
oguardai config validate --config oguardai.yaml

Proxy

cargo build --release -p oguardai-proxy
./target/release/oguardai-proxy --target https://api.openai.com --port 8081

Full Workspace

cargo build --release --workspace && cargo test --workspace

5. Install from Package Managers

Rust CLI (when published):

cargo install oguardai-cli

Python SDK:

pip install oguardai-sdk

TypeScript SDK:

npm install @oguardai/sdk    # or: pnpm add @oguardai/sdk

6. 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/bin

7. 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.target

Generate 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.env
sudo 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 -f

Reverse 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 nginx

8. Cloud Deployments

Audit trail: A non-dev (api_key) start requires a durable, strict audit trail (GUARDAI_AUDIT_BACKEND=file + GUARDAI_AUDIT_FILE_PATH on 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 set GUARDAI_ALLOW_EPHEMERAL_AUDIT=1 to 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-central1

Cloud 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-key

As 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 detector

Local (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 9090

Local (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 9090

The 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:9090

Or: 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

VariableDefaultDescription
GUARDAI_SESSION_SECRET(required)Secret for AES-256-GCM session encryption (16 char minimum)
GUARDAI_HOST0.0.0.0Server bind address
GUARDAI_PORT3000Server listen port
GUARDAI_AUTH_MODEdevAuthentication 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_BACKENDlogAudit 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_STRICTfalseFail 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_LOGguardai_server=infoLog level filter
NER_BACKENDglinerPython detector engine: gliner, spacy, none
GLINER_MODELurchade/gliner_medium-v2.1GLiNER 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/capabilities

Round-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.