# Customer-Managed Air-Gapped Installs

Use this guide when Pretorin is installed from a customer-managed or air-gapped
deployment bundle and the CLI needs to talk to that private platform instead of
the hosted Pretorin service.

The platform bundle owns Kubernetes install, data seeding, embedding, and AI
provider validation. The CLI owns developer and agent access to the installed
platform.

## Security Notes

Do not paste secret values, decoded Kubernetes secrets, API tokens, model API
keys, or full environment dumps into support tickets or chat. The commands below
intentionally check non-secret configuration values or secret key names only.

When validating secrets, confirm that required keys exist without decoding their
values:

```bash
kubectl get secret -n pretorin pretorin-platform-secrets \
  -o go-template='{{range $k, $_ := .data}}{{println $k}}{{end}}'
```

## Platform Validation

After the platform is installed and object storage is initialized, run the
bundle smoke test from the deployment bundle:

```bash
NAMESPACE=pretorin scripts/customer/validate-airgap-install.sh
```

The smoke test runs inside the deployed API pod and verifies:

- database connectivity
- seeded framework, package, source, document requirement, and crosswalk data
- NIST 800-53 catalog presence
- OSCAL control embedding coverage and vector dimensions
- direct local AI provider chat and embedding calls
- API-to-AI service connectivity
- dashboard AI chat streaming and chat message persistence

For staged troubleshooting, skip chat first:

```bash
NAMESPACE=pretorin scripts/customer/validate-airgap-install.sh --skip-chat
```

That confirms data seeding, embeddings, provider reachability, and AI service
health before testing the full streaming chat path.

## CLI Configuration

Point the CLI at the private platform public API endpoint:

```bash
pretorin config set platform_api_base_url https://<platform-host>/api/v1/public
pretorin config set model_api_base_url https://<platform-host>/api/v1/public/model
pretorin whoami
pretorin frameworks list
```

`pretorin config set platform_api_base_url` prompts for an API key for the new
endpoint and validates it before saving the change.

For non-interactive hosts, use environment variables:

```bash
export PRETORIN_API_KEY=<platform-api-token>
export PRETORIN_PLATFORM_API_BASE_URL=https://<platform-host>/api/v1/public
export PRETORIN_MODEL_API_BASE_URL=https://<platform-host>/api/v1/public/model

pretorin whoami
pretorin frameworks list
```

If you use MCP, the MCP server inherits the same CLI configuration:

```bash
pretorin mcp-serve
```

For Codex, Claude, Cursor, and other MCP client setup, see
[MCP Setup Guides](../mcp/setup.md).

## Agent Runtime Notes

For `pretorin agent run`, model credential precedence is:

1. `OPENAI_API_KEY`
2. `config.api_key`
3. `config.openai_api_key`

If you want the agent runtime to use the customer platform model endpoint, keep
`PRETORIN_MODEL_API_BASE_URL` pointed at:

```text
https://<platform-host>/api/v1/public/model
```

If `OPENAI_API_KEY` is set in the shell, it overrides stored Pretorin
credentials for model calls. Unset it when you expect model calls to route
through the customer Pretorin platform.

## Common Issues

| Symptom | Likely cause | First check or fix |
|---------|--------------|--------------------|
| `pretorin whoami` or `pretorin frameworks list` hits hosted Pretorin | CLI base URL is still using hosted config | Run `pretorin config list`, then set `platform_api_base_url` to `https://<platform-host>/api/v1/public`. |
| CLI returns 401 or 403 | API key is for another endpoint, expired, or missing required scopes | Run `pretorin login` or `pretorin config set platform_api_base_url ...` and enter a token from the customer platform. |
| `pretorin agent run` uses the wrong model credentials | Shell `OPENAI_API_KEY` overrides stored CLI credentials | Unset shell `OPENAI_API_KEY`, or intentionally set `PRETORIN_MODEL_API_BASE_URL` for the customer endpoint. |
| `AI provider validation failed` during platform install | `OPENAI_BASE_URL` is wrong, missing `/v1`, or unreachable from the cluster | In the deployment environment, run `python -m app.cli.validate_ai_provider` inside the API pod and confirm DNS, port, TLS, and provider path. |
| Provider calls time out | NetworkPolicy blocks API or AI egress to the local model endpoint | Add matching API and AI NetworkPolicy egress rules, then rerun Helm upgrade. |
| `OPENAI_API_KEY is required` or provider env is missing | Platform secret was not created or was created before env vars were exported | Rerun `scripts/customer/create-customer-secrets.sh`, then restart API and AI deployments. |
| Embedding count is zero or below the control count | The embedding hook failed, is still running, or the API does not have the local embedding model env | Check `pretorin-api-embedding-sync-*` job logs and confirm API env includes `OPENAI_BASE_URL` and `OPENAI_EMBEDDING_MODEL`. |
| Embedding dimension mismatch | Local embedding model does not return 1536-dimensional vectors | Use a 1536-dimension embedding model for `OPENAI_EMBEDDING_MODEL`. |
| Data seeding fails or NIST 800-53 is missing | Framework seed hook did not complete | Check `pretorin-api-seed-frameworks-*` job logs and rerun Helm upgrade after fixing the error. |
| Smoke test passes with `--skip-chat` but fails without it | Provider supports basic calls but not streaming chat, tool calling, or the configured chat model | Confirm the model supports streaming chat completions and tool calls; update `OPENAI_MODEL` if needed. |
| Chat stream emits an error after a session is created | AI service can start the request but failed during agent execution | Check `deployment/pretorin-ai` logs around the smoke-test timestamp. |

Useful platform-side checks:

```bash
kubectl get pods,jobs -n pretorin
kubectl logs -n pretorin job/<failed-job-name>

kubectl exec -n pretorin deployment/pretorin-api -- \
  sh -c 'env | grep -E "^(OPENAI_BASE_URL|OPENAI_MODEL|OPENAI_EMBEDDING_MODEL|OPENAI_USE_RESPONSES|AI_REQUIRE_LOCAL_PROVIDER|AI_PROVIDER_KIND|AI_PROVIDER_DEFAULT_PROFILE_ID|AI_PROVIDER_DEFAULT_PROFILE_NAME|AI_SERVICE_URL)="'

kubectl exec -n pretorin deployment/pretorin-ai -- \
  sh -c 'env | grep -E "^(OPENAI_BASE_URL|OPENAI_MODEL|OPENAI_EMBEDDING_MODEL|OPENAI_USE_RESPONSES|AI_REQUIRE_LOCAL_PROVIDER|AI_PROVIDER_KIND|AI_PROVIDER_DEFAULT_PROFILE_ID|AI_PROVIDER_DEFAULT_PROFILE_NAME|AI_SERVICE_URL)="'
```

## What Passing Looks Like

A healthy install can:

- return `pretorin whoami`
- list seeded frameworks with `pretorin frameworks list`
- run `scripts/customer/validate-airgap-install.sh` without failures
- use chat in the web UI
- route MCP tools through `pretorin mcp-serve`
- route `pretorin agent run` through the configured customer model endpoint
