Skip to content

System Admin Guide

KUKAN supports two deployment methods, described below in order: the simpler Docker Compose (on-premises / isolated networks) first, then AWS (cloud / production).

Deploy on-premises or in isolated networks using Docker Compose. For production use, we recommend the latest release (vX.Y.Z tag).

Terminal window
git checkout vX.Y.Z # latest release
cp .env.prod.example .env.prod
# Edit .env.prod
docker compose --env-file .env --env-file .env.prod --profile prod up -d --build

Check the release notes for breaking changes and migration steps before upgrading.

Terminal window
git fetch --tags
git checkout vX.Y.Z # the new release
docker compose --env-file .env --env-file .env.prod --profile prod up -d --build

On-premises deployments use the following alternative services:

AWS ServiceOn-Premises Alternative
S3MinIO
SQSElasticMQ
OpenSearchPostgreSQL full-text search

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

Data-protection settings are integrated into the scale presets:

Settingsmallmediumlarge
S3 versioning (accidental delete/overwrite guard)
DB automated backup retention (point-in-time)7d14d35d
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)”
  1. Prepare your environment definitions (cp infra/config/environments.example.ts infra/config/environments.ts, then edit environments.ts). Each entry becomes one environment = one pipeline, so delete the ones you don’t need (e.g. remove dev for a single environment)
  2. Create a CodeConnections connection in the AWS console and set its ARN as the top-level connectionArn export in environments.ts (shared by all envs)
    • Open CodeBuildSettings → 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 PENDING until authorized in the browser)
  3. cdk bootstrap (prerequisite for deploy; per account/region, include us-east-1 for the GlobalStack)
  4. 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 set certificateArn/webAclArn on each site entry
  5. Commit environments.ts and cdk.context.json so the CodeBuild synth reads them from the checkout
  6. Deploy the pipeline stack once (npx cdk deploy KukanPipeline)
  7. After that, pushes to each env’s deployBranch deploy automatically (the pipeline self-mutates)

Structure of environments.ts (connectionArn is top-level; per-env settings go inside environments):

infra/config/environments.ts
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.

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.

Terminal window
pnpm db:create-user --email admin@example.com --name admin --password <password> --role sysadmin
OptionDescriptionRequired
--emailEmail addressYes
--nameUsername (lowercase alphanumeric, hyphens, underscores, periods)Yes
--passwordPassword (8+ characters)Yes
--roleuser or sysadmin (default: user)No

System administrators (sysadmin) can manage users from the admin dashboard.

  • Change user display name and role (user / sysadmin)
  • Cannot demote your own role (lockout prevention)
  • Soft-delete (sets state to deleted)
  • All sessions and API tokens are immediately revoked
  • Audit logs and activity history are preserved

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.

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.

SettingDescription
Search example chipsExample queries shown under the search box
Semantic search & similarity thresholdWhether 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 suggestionsFeature toggle (kill switch), generation model ID (blank = provider default), and a connection test
User self-registrationSee “Self-registration control” above

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)CauseFix
not authorized to perform: bedrock:InvokeModelThe task role lacks IAM access to the modelAdd it to bedrock.completionModels in environments.ts and redeploy
use case details have not been submittedThe Anthropic use-case form hasn’t been submittedSubmit the Anthropic “use case details” form in the Bedrock console (once per account; ~15 min to activate)
AccessDenied mentioning aws-marketplace:SubscribeA Marketplace-served model isn’t subscribed on the accountInvoke 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.

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

Key environment variables are listed below. See .env.example for the full list.

VariableDescriptionDefault
POSTGRES_HOSTPostgreSQL hostlocalhost
POSTGRES_PORTPostgreSQL port5432
POSTGRES_DBDatabase namekukan
POSTGRES_USERDatabase userkukan
POSTGRES_PASSWORDDatabase passwordkukan
S3_BUCKETS3 bucket namekukan-dev
S3_ENDPOINTS3 endpoint (MinIO: http://localhost:9000, AWS: omit)-
SEARCH_TYPESearch engine (opensearch / postgres)opensearch
OPENSEARCH_URLOpenSearch endpointhttp://localhost:9200
SQS_QUEUE_URLSQS queue URL-
SQS_ENDPOINTSQS endpoint (ElasticMQ: http://localhost:9324, AWS: omit)-
BETTER_AUTH_SECRETAuth session secret (32+ characters)-
BETTER_AUTH_URLPublic URLhttp://localhost:3000
LOG_LEVELLog level (tracefatal)info