System Admin Guide
Deployment
Section titled “Deployment”KUKAN supports two deployment methods, described below in order: the simpler Docker Compose (on-premises / isolated networks) first, then AWS (cloud / production).
Docker Compose (On-Premises)
Section titled “Docker Compose (On-Premises)”Deploy on-premises or in isolated networks using Docker Compose.
For production use, we recommend the latest release (vX.Y.Z tag).
git checkout vX.Y.Z # latest releasecp .env.prod.example .env.prod# Edit .env.proddocker compose --env-file .env --env-file .env.prod --profile prod up -d --buildUpdating
Section titled “Updating”Check the release notes for breaking changes and migration steps before upgrading.
git fetch --tagsgit checkout vX.Y.Z # the new releasedocker compose --env-file .env --env-file .env.prod --profile prod up -d --buildOn-premises deployments use the following alternative services:
| AWS Service | On-Premises Alternative |
|---|---|
| S3 | MinIO |
| SQS | ElasticMQ |
| OpenSearch | PostgreSQL full-text search |
AWS Deployment
Section titled “AWS Deployment”KUKAN deploys on AWS as CloudFront (WAF + cache) → internal ALB → ECS Fargate. Deployment is automated with CDK Pipelines (AWS CodePipeline): using a CodeConnections (GitHub App) source, a push to the target branch deploys each environment (dev / prd, etc.) as a CDK Stage.
Environments are defined in infra/config/environments.ts (copied from environments.example.ts).
Create environments in the multi-site shape (declare sites). Even with a single site,
starting with one sites entry makes adding sites later non-destructive (see
Multi-Site Operation). The single-site shape — no
sites, with site-specific settings (domain, WAF, etc.) directly on the env entry —
keeps working for backward compatibility but should not be used for new deployments
(the two shapes cannot be mixed). See the
Environment Configuration Reference for the
full parameter list.
Key AWS resources:
- CloudFront — CDN, WAF, TLS termination (the front layer)
- ALB (internal) — load balancer (reachable only via the CloudFront VPC origin)
- ECS Fargate — Web application + Pipeline Worker
- Aurora Serverless v2 / RDS — PostgreSQL database
- S3 — File storage
- SQS — Pipeline queue
- OpenSearch — Full-text search engine
- AWS Backup — Isolated backups (enabled on the large scale etc.)
Backups and data protection
Section titled “Backups and data protection”Data-protection settings are integrated into the scale presets:
| Setting | small | medium | large |
|---|---|---|---|
| S3 versioning (accidental delete/overwrite guard) | — | ✓ | ✓ |
| DB automated backup retention (point-in-time) | 7d | 14d | 35d |
| AWS Backup (isolated custody, long-term) | — | — | daily 35d + monthly 12mo |
Uploaded files are stored in S3 and automatically replicated across multiple availability zones in the region (11-nines design durability). With versioning enabled, accidental deletes and overwrites are also recoverable. AWS Backup keeps copies of S3 and the database in an isolated Backup Vault, covering incidents that take out the resource itself and generations beyond 35 days. Tune per environment via overrides.backup in environments.ts (enabling awsBackup requires s3Versioning: true).
Setup (one-time manual; after that, pushes deploy automatically)
Section titled “Setup (one-time manual; after that, pushes deploy automatically)”- Prepare your environment definitions (
cp infra/config/environments.example.ts infra/config/environments.ts, then editenvironments.ts). Each entry becomes one environment = one pipeline, so delete the ones you don’t need (e.g. removedevfor a single environment) - Create a CodeConnections connection in the AWS console and set its ARN as the top-level
connectionArnexport inenvironments.ts(shared by all envs)- Open CodeBuild → Settings → Connections → Create connection in the left nav (a shared “Developer Tools” setting; reaching it via CodeBuild is easiest) → select GitHub → name it (e.g.
kukan-github, an AWS-side label) → Connect to GitHub → install/authorize the AWS Connector for GitHub app (choose Only select repositories and grant just the repo you deploy from — least privilege) → once status is Available, copy the ARN - The GitHub App authorization requires a browser action (creating via CLI/CDK leaves it
PENDINGuntil authorized in the browser)
- Open CodeBuild → Settings → Connections → Create connection in the left nav (a shared “Developer Tools” setting; reaching it via CodeBuild is easiest) → select GitHub → name it (e.g.
cdk bootstrap(prerequisite for deploy; per account/region, include us-east-1 for the GlobalStack)- For a custom domain / WAF only: create the us-east-1 certificate/WAF once via standalone (
npx cdk deploy -c env=prd Prd/KukanGlobalStack) and setcertificateArn/webAclArnon each site entry - Commit
environments.tsandcdk.context.jsonso the CodeBuild synth reads them from the checkout - Deploy the pipeline stack once (
npx cdk deploy KukanPipeline) - After that, pushes to each env’s
deployBranchdeploy automatically (the pipeline self-mutates)
Structure of environments.ts (connectionArn is top-level; per-env settings go inside environments):
export const connectionArn = 'arn:aws:codeconnections:...:connection/...' // ARN from step 2 (shared by all envs)
export const environments = { // account is required (misdeployment guard); use the same ID across envs for same-account dev: { account: '000000000000', scale: 'small', githubRepo: 'owner/repo', deployBranch: 'develop', sites: [{ name: 'main', enableWaf: false }], }, prd: { account: '000000000000', scale: 'large', githubRepo: 'owner/repo', deployBranch: 'main', sites: [{ name: 'main', enableWaf: false }], },}For details and design rationale, see docs/specs/phase4-deploy.md and ADR-030 (docs/adr/en/030-cdk-pipelines-deploy.md) / ADR-031 (docs/adr/en/031-multi-environment-deploy.md) in the repository.
Initial setup
Section titled “Initial setup”While no users exist yet, self-registration is forced on and the first registered user automatically becomes a sysadmin. After deploying, open the sign-up page in a browser and register the first user.
For headless initialization, or to recover when no sysadmin is left, use the CLI.
pnpm db:create-user --email admin@example.com --name admin --password <password> --role sysadmin| Option | Description | Required |
|---|---|---|
--email | Email address | Yes |
--name | Username (lowercase alphanumeric, hyphens, underscores, periods) | Yes |
--password | Password (8+ characters) | Yes |
--role | user or sysadmin (default: user) | No |
User management
Section titled “User management”System administrators (sysadmin) can manage users from the admin dashboard.
User list & editing
Section titled “User list & editing”- Change user display name and role (user / sysadmin)
- Cannot demote your own role (lockout prevention)
Deleting users
Section titled “Deleting users”- Soft-delete (sets state to deleted)
- All sessions and API tokens are immediately revoked
- Audit logs and activity history are preserved
Self-registration control
Section titled “Self-registration control”Toggle new user self-registration from the admin dashboard (Site Management → User Self-Registration; default: disabled). While disabled, system administrators create users from the admin dashboard. While no users exist, self-registration is enabled regardless of the setting so the instance can be bootstrapped.
Site settings (runtime)
Section titled “Site settings (runtime)”Some settings can be changed from the admin dashboard without redeploying (Site Management → /dashboard/admin/site, system administrators only). Changes are stored in the database, recorded in the audit log, and take up to about 30 seconds to take effect.
| Setting | Description |
|---|---|
| Search example chips | Example queries shown under the search box |
| Semantic search & similarity threshold | Whether to include semantically related results in search, and the minimum “closeness” required. Loosening it yields more hits but more noise; tightening favors precision (requires a configuration where an embedding model is available) |
| AI metadata suggestions | Feature toggle (kill switch), generation model ID (blank = provider default), and a connection test |
| User self-registration | See “Self-registration control” above |
AI and embedding model selection
Section titled “AI and embedding model selection”Semantic search (embeddings) and AI metadata suggestions use the provider chosen by AI_TYPE.
- AWS: Bedrock (
AI_TYPE=bedrock) - On-premises / development: Ollama (
AI_TYPE=ollama) or OpenAI-compatible (AI_TYPE=openai) - Disabled:
AI_TYPE=none(NoOp) — semantic search falls back to keyword search, and AI suggestions are unavailable
The generation/embedding model can be switched from the admin dashboard above without redeploying. Candidate models are set via the AI_COMPLETION_MODELS environment variable (comma-separated). This is the list of models approved for the deployment: across all providers it becomes the admin picker options, with the first entry as the default (the dashboard’s “provider default”). A model being available on the server does not put it in the picker — only listed models appear. On Bedrock, CDK injects this variable from bedrock.completionModels in environments.ts (the models granted InvokeModel in IAM), so add candidates there and redeploy. On Ollama, Docker Compose’s ollama-init pulls every listed model at startup, so listed models become available automatically. When unset, only the built-in default model is offered (Ollama: gemma4:e4b / OpenAI-compatible: gpt-4o-mini). To run Ollama on a GPU, see the Getting Started guide.
When AI suggestions don’t work (Bedrock)
Section titled “When AI suggestions don’t work (Bedrock)”The connection test in Site Management runs one attempt through the same path as real generation (structured output) and, on failure, shows the error plus a fix hint. Common Bedrock-specific causes:
| Symptom (connection-test error) | Cause | Fix |
|---|---|---|
not authorized to perform: bedrock:InvokeModel | The task role lacks IAM access to the model | Add it to bedrock.completionModels in environments.ts and redeploy |
use case details have not been submitted | The Anthropic use-case form hasn’t been submitted | Submit the Anthropic “use case details” form in the Bedrock console (once per account; ~15 min to activate) |
AccessDenied mentioning aws-marketplace:Subscribe | A Marketplace-served model isn’t subscribed on the account | Invoke the model once to enable it — see “First-time subscription for Marketplace models” below. If an organization SCP restricts it, grant aws-marketplace:Subscribe / ViewSubscriptions from the management account (an SCP overrides even AdministratorAccess) |
All of these are out-of-app AWS setup — one-time console actions. Note that Anthropic models (Claude, etc.) require both the use-case form and a Marketplace subscription, whereas Amazon Titan / Nova need neither (both auto-enable on first invocation).
First-time subscription for Marketplace models
Section titled “First-time subscription for Marketplace models”AWS Marketplace-served models such as Cohere Embed v4 are not auto-enabled: to make one usable account-wide, a user with AWS Marketplace permissions must invoke it once. A single invocation via either method below enables it for the deployed task roles as well (propagation takes a few minutes).
-
Console: open the model from Bedrock → Model catalog and run any input once in the Playground. Enabled subscriptions are listed under AWS Marketplace → “Manage subscriptions”.
-
CLI (the same thing in one command):
Terminal window aws bedrock-runtime invoke-model --region ap-northeast-1 \--model-id cohere.embed-v4:0 --content-type application/json \--body '{"texts":["test"],"input_type":"search_document"}' /dev/stdout
If you switch the embedding model, run a bulk re-embed (search-index rebuild) to apply it to existing data.
Health checks
Section titled “Health checks”KUKAN automatically monitors the health of external URL resources.
- Periodic automated checks via scheduler
- View health check results from the admin dashboard
- Identify and address failing resources
Environment variables
Section titled “Environment variables”Key environment variables are listed below. See .env.example for the full list.
| Variable | Description | Default |
|---|---|---|
POSTGRES_HOST | PostgreSQL host | localhost |
POSTGRES_PORT | PostgreSQL port | 5432 |
POSTGRES_DB | Database name | kukan |
POSTGRES_USER | Database user | kukan |
POSTGRES_PASSWORD | Database password | kukan |
S3_BUCKET | S3 bucket name | kukan-dev |
S3_ENDPOINT | S3 endpoint (MinIO: http://localhost:9000, AWS: omit) | - |
SEARCH_TYPE | Search engine (opensearch / postgres) | opensearch |
OPENSEARCH_URL | OpenSearch endpoint | http://localhost:9200 |
SQS_QUEUE_URL | SQS queue URL | - |
SQS_ENDPOINT | SQS endpoint (ElasticMQ: http://localhost:9324, AWS: omit) | - |
BETTER_AUTH_SECRET | Auth session secret (32+ characters) | - |
BETTER_AUTH_URL | Public URL | http://localhost:3000 |
LOG_LEVEL | Log level (trace–fatal) | info |