This guide covers two production paths: running the global npm package against your own
PostgreSQL and Redis, and provisioning the bundled AWS stack with the repository's
installer. For a single-machine trial, use [Get started](/guides/getting-started/)
instead; for a container deployment, see [Deploy with Docker](/guides/deploy-docker/).
Bootstrap credentials, single sign-on, and safe upgrades are covered in
[Operate a self-hosted Chorus](/guides/deploy-operations/).

## Run the global package against external PostgreSQL

Install the Chorus command globally on the host that will run the web application:

```sh
npm install --global @chorus-aidlc/chorus
```

Point Chorus at an existing PostgreSQL database with `DATABASE_URL` and supply the
required authentication and secret variables, then start it:

```sh
DATABASE_URL='postgresql://chorus:password@db.internal:5432/chorus' \
NEXTAUTH_SECRET="${NEXTAUTH_SECRET:?Set a persistent random secret first}" \
SUPER_ADMIN_EMAIL='admin@example.com' \
SUPER_ADMIN_PASSWORD_HASH='$2b$10$...' \
chorus
```

On startup Chorus applies any pending database migrations automatically
(`prisma migrate deploy`) and then serves the web application on port `8637`. The database must already exist and be reachable; Chorus
does not create the database for you on this path.

The variables Chorus reads in a production run:

| Variable | Required | Description |
| --- | --- | --- |
| `DATABASE_URL` | Yes | PostgreSQL connection string, `postgresql://user:password@host:port/dbname`. |
| `NEXTAUTH_SECRET` | For multiple instances | Signing secret shared by all replicas. For a single npm instance, it may be omitted: `chorus` persists one in `~/.chorus-data/.secret` by default. Known placeholders in the environment are ignored with a warning; a persisted placeholder or empty key stops startup. |
| `SUPER_ADMIN_EMAIL` | Yes | Email that bootstraps the `/admin` Super Admin panel. |
| `SUPER_ADMIN_PASSWORD_HASH` | Yes | Bcrypt hash of the Super Admin password. |
| `DEFAULT_USER` | Optional | Enables a built-in email/password login for first access. |
| `DEFAULT_PASSWORD` | Optional | Password for the built-in login. |
| `REDIS_URL` | Optional | Redis connection string. Required once you run more than one instance (see below). |

See [Operate a self-hosted Chorus](/guides/deploy-operations/) for how these bootstrap
credentials work, how to generate the bcrypt hash, and how to turn the built-in login off.

:::caution[Handle secrets carefully]
`NEXTAUTH_SECRET` and the Super Admin credentials grant full access. Keep them out of shell
history and source control — supply them through your process manager, systemd unit, or a
secret store rather than typing them inline on a shared host.
:::

## Run more than one instance

Chorus propagates live updates (the SSE event stream) across instances through Redis. Set
`REDIS_URL` to a shared Redis so every instance publishes and receives the same events:

```sh
REDIS_URL='redis://default:password@redis.internal:6379'
```

**Redis is required for cross-instance event propagation.** Without `REDIS_URL`, Chorus
falls back to an in-memory event bus that is scoped to a single process — updates made on
one instance never reach the others. A single-instance deployment can run without Redis on
that in-memory fallback, but any deployment with more than one instance must configure a
shared Redis.

All instances must also share the same secure `NEXTAUTH_SECRET`, so that Chorus-signed
sessions remain valid whichever instance receives a request.

## Deploy the AWS CDK stack

Chorus bundles an AWS CDK stack under `packages/chorus-cdk` and a repository-root
`install.sh` that drives it interactively. The stack stands up a complete, HTTPS-fronted
deployment with managed PostgreSQL and Redis.

### Prerequisites

`install.sh` checks for `aws`, `node`, `pnpm`, and valid AWS credentials. Configure
`aws configure` or `AWS_PROFILE` first. Certificate requirements depend on the mode below.

### Choose the deployment mode

- **`alb` (default)**: public ALB with HTTPS on port 443; requires an ACM certificate in
  the deployment region. A custom domain is optional.
- **`cloudfront`**: public HTTPS through CloudFront, forwarding to a private ALB on HTTP
  port 80 through a VPC origin. No certificate is needed for the default `*.cloudfront.net`
  domain. For a custom domain, supply both the domain and an ACM certificate in
  **`us-east-1`**. Caching is disabled for authenticated pages and API responses.

Choose the mode in `./install.sh`, or pass `-c deployMode=cloudfront` to CDK.
`default_deploy.sh` preserves the choice. Non-interactive installs use
`CHORUS_INSTALL_NONINTERACTIVE=1`, `DEPLOY_MODE=alb|cloudfront`, and the other required
installer variables. Neither mode creates DNS records: point a custom domain at the
`AlbDnsName` or `CloudFrontDomainName` stack output, and update the OIDC callback URL.

:::caution[Switching modes on an existing deployment]
Changing modes replaces the ALB and changes its DNS name. Deploy a new stack, migrate data
and DNS, and verify it before retiring the old stack. Keeping `alb` preserves the existing
topology. In `cloudfront` mode the ALB is private: diagnose through the CloudFront URL, ECS
logs, or target-group health. Confirm your region supports CloudFront VPC origins.
:::

### Run the installer

From the repository root:

```sh
./install.sh
```

The installer prompts for the deployment configuration:

| Prompt | Required | Notes |
| --- | --- | --- |
| Deployment mode | No | `alb` (default) or `cloudfront`. |
| Stack name | No | Defaults to `Chorus`. |
| ACM Certificate ARN | Mode-dependent | Required in the deployment region for `alb`; required in `us-east-1` only with a custom domain for `cloudfront`. |
| Custom domain | No | For `cloudfront`, pair with a `us-east-1` certificate; otherwise use the distribution domain. |
| Super admin email | Yes | Bootstraps the `/admin` Super Admin account. |
| Super admin password | Yes | Minimum 8 characters. Hashed with bcrypt at synth time; the plaintext is never stored in the template. |
| NextAuth secret | No | Generated at each synth if left blank. Supply one persistent value to keep sessions valid across redeployments. |

The stack shares `NEXTAUTH_SECRET` through Secrets Manager, but does not read the existing
value back during synth. Reuse the same value in the installer or CDK's `nextAuthSecret`
context on every deployment. If you previously left it blank, preserve the existing
Secrets Manager value before redeploying to avoid rotating it.

After collecting the answers, `install.sh` installs and builds the CDK package, bootstraps
the CDK environment, and deploys the stack. It also writes a re-runnable
`default_deploy.sh` with your answers so later deploys do not re-prompt.

### What the stack provisions

The stack creates:

| Component | What it is | Key configuration |
| --- | --- | --- |
| Application Load Balancer | Public HTTPS:443 in `alb`; private HTTP:80 origin in `cloudfront`. | Routes to ECS; 60-minute idle timeout. |
| CloudFront | Public HTTPS endpoint, only in `cloudfront` mode. | Private VPC origin; caching disabled; optional custom domain. |
| ECS Fargate service | Runs the Chorus container in private subnets. | Desired count 2, 1024 CPU units / 2048 MiB, deployment circuit breaker with automatic rollback. |
| Aurora Serverless v2 (PostgreSQL) | Managed PostgreSQL cluster. | Encrypted storage; credentials auto-generated into Secrets Manager; 1-day backup retention. |
| ElastiCache Serverless (Redis) | Managed Redis for cross-instance events. | RBAC user; password stored in Secrets Manager. |
| Secrets Manager | Holds the generated database and Redis credentials plus the app secrets. | Injected into the ECS task at runtime. |

### Deploy, update, and destroy

| Action | Command |
| --- | --- |
| First deploy | `./install.sh` |
| Re-deploy or update | `./default_deploy.sh` (or `pnpm cdk:deploy`) |
| Destroy | `pnpm cdk:destroy` |

`packages/chorus-cdk` is the authoritative source for the stack. Read it there when you
need exact resource definitions or want to adjust capacity, retention, or networking.
