AWS (ECS Fargate)
This page sets up archmax automation on AWS: the image as an ECS Fargate service behind an Application Load Balancer (ALB), people signing in through the load balancer and Amazon Cognito, credentials kept in AWS Secrets Manager, the design agent on Amazon Bedrock and S3 through the task’s IAM role, and Redis either on Amazon MemoryDB or inside the container. What every deployment needs regardless of where it runs is in installation and operating a deployment.
What runs where
Section titled “What runs where”| Piece | On AWS |
|---|---|
| The image (nginx, the API, the dispatcher) | One ECS Fargate service, container port 8080 |
| Workers | In those tasks, or on a worker-host service of their own, with no task role (see Worker hosts) |
| Sign-in | The ALB’s authenticate-oidc action against a Cognito user pool (AWS ALB sign-in), or the web app itself against Cognito or Auth0 (OIDC sign-in) |
| Workspace storage | One S3 bucket, reached with the task role |
| The design agent’s model | Bedrock, reached with the task role |
| Redis | MemoryDB, or the container’s own Redis when REDIS_URL is unset |
| MongoDB | Any MongoDB the tasks can reach, such as MongoDB Atlas over VPC peering or PrivateLink |
| Credentials’ secrets | Secrets Manager, one secret per credential under archmax/credentials/, reached with the task role |
| The deployment’s own secrets | Secrets Manager or SSM Parameter Store, injected when a task starts |
Choosing how people sign in
Section titled “Choosing how people sign in”- AWS ALB sign-in (
ARCHMAX_SIGN_IN=aws-alb). The load balancer signs people in at Cognito before any request reaches the platform, and forwards who they are in the header it signs,x-amzn-oidc-data. The platform verifies that header with the load balancer’s public key and checks that your load balancer signed it. The web app holds no token. This is the setup the rest of this page describes. - OIDC sign-in (
ARCHMAX_SIGN_IN=oidc). The web app signs in at Cognito or Auth0 itself and sends its access token; the load balancer only forwards. See OIDC sign-in on AWS at the end of this page.
Where credentials are kept
Section titled “Where credentials are kept”Every credential an owner stores — an API key, an OAuth token, an endpoint token’s digest — has
secret fields, and ARCHMAX_SECRET_STORE names where the deployment keeps them:
- AWS Secrets Manager (
ARCHMAX_SECRET_STORE=aws-secrets-manager, recommended on AWS). Each credential’s secret fields are one secret, named<prefix><credential id>; MongoDB keeps only the secret’s name and the version the platform wrote. There is no master key to generate or keep. Every read and write shows in CloudTrail, the secrets are encrypted with the account’saws/secretsmanagerkey or a key of your own, and the task role reaches only the prefix. - The database (
db, or unset). The secret fields are sealed with AES-256-GCM underMASTER_KEYon the credential’s record in MongoDB, as on any other host.
The AWS store’s variables:
| Variable | Required | What it is |
|---|---|---|
ARCHMAX_AWS_SECRETS_MANAGER_REGION |
yes | The region the secrets live in, such as eu-west-1. |
ARCHMAX_AWS_SECRETS_MANAGER_PREFIX |
no | What every secret’s name begins with; archmax/credentials/ by default. It ends in /. |
ARCHMAX_AWS_SECRETS_MANAGER_KMS_KEY_ID |
no | A customer-managed KMS key (id, ARN or alias) new secrets are encrypted with. |
- One prefix per deployment, chosen once. The platform creates, reads, writes and deletes
only secrets under its prefix, and never lists any, so two deployments in one account need two
prefixes that don’t nest (
archmax/prod/credentials/andarchmax/staging/credentials/). The default stays clear ofarchmax/master-keyandarchmax/connectionsbelow, which the execution role reads and the task role must not. - A deleted credential’s secret is scheduled for deletion with a seven-day recovery window, and so is every secret of a deleted environment or agent. A live credential’s secret is never deleted: each change writes a new version of it.
- Secrets Manager bills per secret and per 10,000 calls. The platform keeps a value it read in memory for up to five minutes, so a credential costs about one call per five minutes per task while it is in use.
The Cognito user pool
Section titled “The Cognito user pool”Create a user pool and, in it, an app client for the load balancer:
- The issuer. Use the pool’s classic issuer,
https://cognito-idp.<region>.amazonaws.com/<pool-id>. The newerissuer-cognito-idpform doesn’t work with ALB authentication. - The app client has a client secret and the authorization code grant, with the scopes
openid email profile. Its callback URL ishttps://<host>/oauth2/idpresponse(the load balancer’s own) and its sign-out URL ishttps://<host>/signed-out, the page the web app shows after signing out without a session. - Readable attributes. The load balancer signs Cognito’s userinfo answer, which carries the
attributes the app client may read: allow
email,email_verified,nameandcustom:managed_domain. - A short access-token lifetime, such as 15 minutes. Signing out expires the load balancer’s session cookies, but a cookie someone copied stays usable until the access token inside it expires.
- Self sign-up off, unless anyone may create an account. Invitations and the waitlist decide who joins an organization, but a pool with self sign-up lets anyone sign in.
Verified email addresses
Section titled “Verified email addresses”The platform admits only a person whose email the identity provider says is verified, because
invitations, taking over an account by email and auto-onboarding all trust that address.
Cognito’s own users verify theirs at sign-up. Federated users carry email_verified only when
the federation maps it:
- Google: map Google’s
email_verifiedtoemail_verified. - Microsoft Entra ID: Entra sends no
email_verified, and itsemailis an attribute a directory’s administrator can set to any address. Don’t map a blanket “verified”: any Entra directory could then claim any address. Restrict the federation to your own directories (each directory’s own issuer,https://login.microsoftonline.com/<directory-id>/v2.0), turn self sign-up off, and setARCHMAX_EMAIL_VERIFICATION=off, which vouches for every address the identity provider supplies. That is safe only while the pool admits no address you don’t control. Otherwise, sign Entra users in through OIDC sign-in with Auth0, whose post-login Action checks that the email equals the user’s principal name.
A person the platform refuses sees a screen asking them to verify their address, so a missing mapping shows up at the first sign-in.
The managed domain
Section titled “The managed domain”Auto-onboarding offers a newcomer membership when the identity provider
asserts which domain manages their account. Cognito exposes custom attributes as custom:<name>,
so add a custom attribute managed_domain, fill it from the federation’s attribute mapping
(Google’s hd, the domain of a Google Workspace account), and tell the platform its name:
ARCHMAX_MANAGED_DOMAIN_CLAIM=custom:managed_domainMake the attribute readable but not writable by the app client, so nobody can set their own.
The load balancer
Section titled “The load balancer”An internet-facing ALB serving HTTPS on port 443 (an ACM certificate), and redirecting HTTP on port 80 to it. Set its idle timeout to 300 s: a binary AG-UI stream and an MCP tool call without progress can stay silent longer than the default 60 s, and nginx in the image cuts at 300 s anyway.
The load balancer’s rules
Section titled “The load balancer’s rules”The rules for HTTPS, most specific first. “authenticate” is an authenticate-oidc action
against the user pool’s app client, followed by a forward to the target group:
| Path | Action | On an unauthenticated request | Why |
|---|---|---|---|
/api/webhooks/*, /api/callbacks/* |
forward | — | Called server to server; no person signs in |
/api/health, /api/sign-in-settings, /api/branding, /api/sign-out |
forward | — | Public by design: the signed-out page wears the deployment’s branding, and signing out must work after the session expired |
/signed-out, /assets/*, /fonts/*, /favicon*, /archmax-* |
forward | — | The page signing out returns to, and the static files it needs; none of them secret |
/api/oauth2/* |
forward | — | A connection’s provider sends the browser back here, including an administrator granting consent who never signed in to the platform (the callback checks its own state), and an authorization server fetches the platform’s client metadata document here |
/api/agui/*, /api/mcp/* |
authenticate | allow |
Exposed endpoints admit callers who never signed in; a member’s session still identifies them |
/api/* |
authenticate | deny |
A request from the web app gets a 401 it can act on, not a redirect it can’t follow |
| default | authenticate | authenticate |
The web app itself |
deny answers 401 when a request carries no session, but a request whose session expired is
still redirected to Cognito. The web app sends its requests without following redirects and
treats either answer as a lost session: it reloads the page once, so the load balancer signs the
person in again, and shows a “sign-in failed” screen if the reload doesn’t help.
Target group and security groups
Section titled “Target group and security groups”- Target group: target type
ip, HTTP on port8080, health check path/healthzexpecting200./healthzreports readiness: that the API answers and, when the container runs its own Redis, that this container’s dispatcher holds the single-dispatcher lease. - Deregistration delay: 60 s with MemoryDB, 15 s with the container’s own Redis (see Redis).
- Security groups: the load balancer’s admits
443(and80) from the clients you serve; the tasks’ admits8080from the load balancer’s security group only. The platform verifies every header the load balancer signs, so a request that bypassed it carries no identity, but nothing else should reach the tasks.
Routes services outside must reach
Section titled “Routes services outside must reach”Most of the platform is for signed-in people, but some routes are called by services and clients
that never sign in. Each of them authenticates its caller itself — the secret token in its path,
the endpoint’s incoming connection, a webhook’s signature, the OAuth2 flow’s state — which is why
the load balancer’s rules above let them past its sign-in. If the deployment is reachable
only from inside your network, through an internal load balancer, an IP restriction, a security
group or AWS WAF, open exactly these to whoever must call them:
| Route | Called by | Needed for | Must be reachable from |
|---|---|---|---|
POST /api/webhooks/<token> |
The sending system’s servers: any service that posts to a webhook trigger | Webhook triggers | The sender’s network; usually the internet |
/api/callbacks/<token> (GET, HEAD, POST, PUT, PATCH, DELETE) |
The service of a piece’s callback trigger, which the platform registers there when the trigger activates (some services first check the address with a GET) |
Callback triggers | The service’s servers; usually the internet |
GET /api/oauth2/callback |
The browser of the person connecting an OAuth2 connection, or of the administrator granting consent, coming back from the provider | OAuth2 connections | Wherever those people’s browsers are — not the provider’s servers |
GET /api/oauth2/client-metadata |
The authorization server of a Remote MCP Server connection authenticated with OAuth, reading who the platform is as a client | OAuth connections to Remote MCP Servers | That authorization server’s servers; usually the internet |
/api/agui/<token> (POST, OPTIONS) |
AG-UI clients: the chat widget in the browsers of a site’s visitors, or another application | Exposed AG-UI endpoints | Wherever those callers are |
/api/mcp/<token>, /api/mcp/<token>/.well-known/oauth-protected-resource |
MCP clients: assistants, IDEs, other agents | Exposed MCP endpoints | Wherever those clients run |
APP_BASE_URLmust be the address these callers use. The platform registers callback addresses with it and names an MCP endpoint’s address by it, so behind a split-horizon setup it is the outside name, not the internal one. The web app shows webhook and endpoint addresses on the origin it is served from.- Senders’ IP ranges. Behind an IP restriction, open the webhook and callback paths to the
ranges the senders publish (GitHub’s
hooksranges in its meta API, Stripe’s webhook addresses, and so on), rather than to everyone. - AWS WAF. The common rule set’s body size rule blocks bodies over 8 KB, and webhook and callback bodies may be up to 256 KiB. Scope that rule down, or exclude these two paths from it.
- An air-gapped deployment works without any of these routes: webhook and callback triggers, and endpoints called from outside, are then simply unavailable. Schedule and polling triggers still run, since the platform calls out for them (if it may call out at all, below).
What the platform calls out to
Section titled “What the platform calls out to”A deployment that isn’t air-gapped reaches these from the tasks, through a NAT gateway or an egress proxy, or through VPC endpoints where AWS offers them:
| Destination | Why | Route it through |
|---|---|---|
public-keys.auth.elb.<region>.amazonaws.com |
The keys the load balancer signs x-amzn-oidc-data with; AWS ALB sign-in refuses everyone without it |
NAT or an egress proxy (a public AWS endpoint) |
bedrock-runtime.<region>.amazonaws.com |
The design agent on Bedrock | A Bedrock interface endpoint, or NAT |
| S3 | Workspace storage | An S3 gateway endpoint |
| MongoDB, MemoryDB | Data and queues | Inside the VPC, peering or PrivateLink |
| Secrets Manager, SSM, ECR, CloudWatch Logs | Starting the task (its secrets, its image and its log) and, with the AWS store, the credentials’ secrets while it runs | Interface endpoints (ECR also needs the S3 gateway endpoint), or NAT |
| Your identity provider’s issuer | OIDC sign-in only: its discovery document, keys and userinfo | NAT |
| What owners configure (LLM providers, MCP servers, the services connections reach, OAuth2 providers’ token endpoints) and the config’s skill registries (git over HTTPS) | Running agents and their tools | NAT or an egress proxy |
The platform refuses outbound requests to private addresses, so every service an owner configures, and the identity provider, must be reachable at a public address.
IAM roles
Section titled “IAM roles”The task role
Section titled “The task role”What the platform itself calls with the task’s credentials: S3, the credentials’ secrets in
Secrets Manager, and, for the design agent, Bedrock.
Grant Bedrock on the inference profile and on the foundation model in every region the
profile routes to, since an eu.* profile spans several EU regions:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"], "Resource": [ "arn:aws:bedrock:eu-west-1:<account>:inference-profile/eu.anthropic.<model-id>", "arn:aws:bedrock:*::foundation-model/anthropic.<model-id>" ] }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::<bucket>/*" }, { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::<bucket>" }, { "Effect": "Allow", "Action": [ "secretsmanager:CreateSecret", "secretsmanager:GetSecretValue", "secretsmanager:PutSecretValue", "secretsmanager:DeleteSecret" ], "Resource": "arn:aws:secretsmanager:eu-west-1:<account>:secret:archmax/credentials/*" } ]}The Secrets Manager statement is the whole of what the AWS store
needs: no ListSecrets, and nothing outside the prefix. Leave it out with the database store. With
ARCHMAX_AWS_SECRETS_MANAGER_KMS_KEY_ID set, also grant kms:GenerateDataKey and kms:Decrypt on
that key; the account’s aws/secretsmanager key needs no grant.
Name the model in the config by its system inference profile ID (eu.anthropic.…), not by an
application inference profile ARN: prompt caching applies only to the former.
designAgent: provider: bedrock region: eu-west-1 model: eu.anthropic.claude-sonnet-5-5 # The model's Bedrock prices in USD per million tokens, required with the section. # Illustrative: enter those of the profile you name (`eu.`, `us.` and `global.` are # priced separately), from Bedrock's current price list. prices: input: 2.20 output: 11.00 cacheRead: 0.22 cacheWrite: 2.75The account must have access to the model: Anthropic’s models on Bedrock ask for a one-time use case form in the Bedrock console before the first call, and until then every design-agent turn fails with an access error.
A bucket encrypted with a customer-managed KMS key also needs kms:GenerateDataKey and
kms:Decrypt on that key. With no S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY, S3 is signed with
the task role too. Owner
connections to Bedrock never use the task role; they carry their own keys.
The execution role
Section titled “The execution role”What ECS needs to start the task: the managed AmazonECSTaskExecutionRolePolicy (the image and
the log), plus secretsmanager:GetSecretValue on the secrets and ssm:GetParameters on the
parameters the task definition names, and kms:Decrypt for a customer-managed key.
The image
Section titled “The image”Every release is published to GitHub Container Registry, ghcr.io/archmax-ai/automation, for
linux/amd64 and linux/arm64, so a task runs on either Fargate architecture
(runtimePlatform.cpuArchitecture X86_64 or ARM64). The package is private until an owner
makes it public, so ECS pulls it one of two ways:
- Copied into ECR (recommended). Copy each release into a repository of your account, after
docker loginto both registries:docker buildx imagetools create --tag <account>.dkr.ecr.<region>.amazonaws.com/archmax:<X.Y.Z> ghcr.io/archmax-ai/automation:<X.Y.Z>. The tasks then pull from your own account, through ECR’s interface endpoints when they have no NAT, and the execution role’s managed policy already allows it. - From GHCR directly. Keep a GitHub token that can read packages in Secrets Manager as
{"username": "<github-user>", "password": "<token>"}, name that secret in the container definition’srepositoryCredentials.credentialsParameter, and allow the execution rolesecretsmanager:GetSecretValueon it. The tasks need outbound access toghcr.io.
Name a release by its tag (1.5.0), or by its digest to pin it exactly.
The task definition
Section titled “The task definition”Secrets come from Secrets Manager (a whole secret, or one key of a JSON secret) or from SSM
Parameter Store, read once when a task starts, so a rotated value needs a new deployment.
environmentFiles loads a .env file from S3 if you prefer one file:
{ "family": "archmax", "requiresCompatibilities": ["FARGATE"], "networkMode": "awsvpc", "taskRoleArn": "arn:aws:iam::<account>:role/archmax-task", "executionRoleArn": "arn:aws:iam::<account>:role/archmax-execution", "containerDefinitions": [ { "name": "archmax", "image": "ghcr.io/archmax-ai/automation:<X.Y.Z>", "portMappings": [{ "containerPort": 8080 }], "linuxParameters": { "capabilities": { "drop": ["ALL"] } }, "stopTimeout": 120, "environment": [ { "name": "NODE_ENV", "value": "production" }, { "name": "APP_BASE_URL", "value": "https://<host>" }, { "name": "OAUTH2_REDIRECT_URL", "value": "https://<host>/api/oauth2/callback" }, { "name": "ARCHMAX_SIGN_IN", "value": "aws-alb" }, { "name": "ARCHMAX_AWS_ALB_ARN", "value": "arn:aws:elasticloadbalancing:eu-west-1:<account>:loadbalancer/app/<name>/<id>" }, { "name": "ARCHMAX_AWS_ALB_SIGN_OUT_URL", "value": "https://<domain>.auth.eu-west-1.amazoncognito.com/logout?client_id=<client-id>&logout_uri=https%3A%2F%2F<host>%2Fsigned-out" }, { "name": "ARCHMAX_MANAGED_DOMAIN_CLAIM", "value": "custom:managed_domain" }, { "name": "ARCHMAX_SECRET_STORE", "value": "aws-secrets-manager" }, { "name": "ARCHMAX_AWS_SECRETS_MANAGER_REGION", "value": "eu-west-1" }, { "name": "S3_BUCKET", "value": "<bucket>" }, { "name": "S3_REGION", "value": "eu-west-1" }, { "name": "ARCHMAX_TRUSTED_PROXIES", "value": "<the VPC's CIDR range>" } ], "secrets": [ { "name": "MONGODB_URI", "valueFrom": "arn:aws:secretsmanager:eu-west-1:<account>:secret:archmax/connections-<suffix>:MONGODB_URI::" }, { "name": "REDIS_URL", "valueFrom": "arn:aws:secretsmanager:eu-west-1:<account>:secret:archmax/connections-<suffix>:REDIS_URL::" }, { "name": "ARCHMAX_CONFIG", "valueFrom": "arn:aws:ssm:eu-west-1:<account>:parameter/archmax/config" } ], "environmentFiles": [{ "value": "arn:aws:s3:::<config-bucket>/archmax.env", "type": "s3" }], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/archmax", "awslogs-region": "eu-west-1", "awslogs-stream-prefix": "archmax" } } } ]}stopTimeout: 120, Fargate’s maximum: on a stop the container drains, letting running turns finish within that time.ARCHMAX_TRUSTED_PROXIESnames the VPC’s range, which the load balancer’s addresses are in: the API’s per-caller rate limits then count each client, not the load balancer.linuxParametersdroppingALL: the image needs nothing it removes, and with it no process the task starts, its health check included, can read another’s environment (see the image).- No
MASTER_KEYwith the AWS store: the deployment refuses to start with one set besideARCHMAX_SECRET_STORE=aws-secrets-manager. With the database store, add{ "name": "MASTER_KEY", "valueFrom": "arn:aws:secretsmanager:eu-west-1:<account>:secret:archmax/master-key-<suffix>" }tosecretsand leave outARCHMAX_SECRET_STOREandARCHMAX_AWS_SECRETS_MANAGER_REGION. ARCHMAX_CONFIGis one line of JSON (pnpm config:envproduces it from aconfig.yaml). It fits a standard SSM parameter up to 4 KB, an advanced one up to 8 KB, or Secrets Manager up to 64 KB. Its${{VAR}}placeholders pass through ECS untouched.- AWS AppConfig and AWS Config are not sources. AppConfig doesn’t inject environment variables (it needs its agent at runtime, which the platform doesn’t read), and AWS Config is a compliance inventory, not a configuration store.
Moving a running deployment’s credentials to Secrets Manager
Section titled “Moving a running deployment’s credentials to Secrets Manager”A deployment that started with the database store moves its credentials’ secrets with a tool, run once with the image’s code against the deployment’s MongoDB:
-
Add the Secrets Manager statement to the task role, and give the role you run the tool with the same permissions.
-
Stop the service (desired count 0), so no task writes a credential while it moves.
-
With the deployment’s variables (
MONGODB_URI,MASTER_KEY, …) and the AWS store’s set beside them, see what would move, then move it:Terminal window ARCHMAX_AWS_SECRETS_MANAGER_REGION=eu-west-1 \pnpm --filter @archmax/core exec tsx tools/move-secrets.ts --to aws-secrets-managerARCHMAX_AWS_SECRETS_MANAGER_REGION=eu-west-1 \pnpm --filter @archmax/core exec tsx tools/move-secrets.ts --to aws-secrets-manager --applyA credential reported as changed while moving is moved by running it again; a run that finds nothing left to move changes nothing.
-
In the task definition, set
ARCHMAX_SECRET_STORE=aws-secrets-managerand the region, removeMASTER_KEY, and start the service. -
Once the deployment works, delete the master key’s secret.
Moving back is the same with --to db and a MASTER_KEY set beside the AWS store’s variables. Do
it before going back to a release older than the AWS store: such a release reads only the database.
Worker hosts
Section titled “Worker hosts”Every turn and every call through a connection runs in a worker. By default the tasks above start them as processes of their own, which shares the task’s network and its task role’s endpoint with code the platform does not control. A deployment serving more than one organization runs them on a worker-host service instead, where neither reaches anything:
- The service. The same image, task definition command
["worker-host"], container port7340, andlinuxParametersdroppingALL. NotaskRoleArn: without a task role, a task has no credentials endpoint to call. The execution role reads its one secret. - Its one secret.
ARCHMAX_WORKER_HOST_KEY(openssl rand -base64 32), in Secrets Manager, given to the worker hosts and to the platform’s tasks alike. The host refuses to start with anything else of the platform’s in its environment: noMONGODB_URI, noREDIS_URL, noS3_*, noARCHMAX_CONFIG. - The platform’s tasks add
ARCHMAX_WORKER_HOST(<name>:7340, through Service Connect, Cloud Map or an internal Network Load Balancer passing TCP through) and the key. Every connection is TLS, authenticated by the key both ways, so the load balancer needs no certificate. - Its security group admits
7340from the platform tasks’ security group only. Its outbound rules reach the internet through the NAT gateway, for the model providers and the APIs the collections call, but nothing in the VPC: MemoryDB’s, MongoDB’s endpoint’s and the VPC endpoints’ security groups admit the platform tasks’ security group, never this one. - Capacity. Each host runs as many workers as its dispatchers send it. Scale the
service on CPU and memory, and keep the platform tasks’
ARCHMAX_WORKER_TURN_SLOTSandARCHMAX_WORKER_CALL_SLOTSwithin what the hosts hold together.
Rolling back to a release without worker hosts simply stops using them.
Choose one of two setups.
MemoryDB
Section titled “MemoryDB”For more than one task, and deploys without downtime. MemoryDB keeps queued jobs, trigger schedules and in-flight state across node failures and deploys.
- A MemoryDB cluster in the tasks’ VPC, with a security group that admits the tasks only.
- An ACL user. Its URL,
rediss://<user>:<password>@clustercfg.<name>.memorydb.<region>.amazonaws.com:6379, goes into Secrets Manager and reaches the task asREDIS_URL. The platform detects cluster mode from the server. - A parameter group with
maxmemory-policyset tonoeviction, which the job queue requires. - The service deploys as ECS’s rolling default, with any
desiredCount, and a 60 s deregistration delay.
ElastiCache works with the same settings, with cluster mode on or off.
The container’s own Redis
Section titled “The container’s own Redis”For one task, without a Redis to run: leave REDIS_URL out, and the container starts a Redis of
its own that serves only its own API and dispatcher. Its state lives on the task’s ephemeral
storage, so a task replacement loses what was only in Redis: trigger firings accepted but not yet
run, OAuth2 connects in progress and live chat streams. Turns and evaluations it was running end
as failed, as do environment operations queued or running; queued turns and evaluations and
provisioning are enqueued again, and schedules are restored. See
the image for the full list.
Only one such container may serve at a time, so set the service to stop the old task before it starts the new one:
| Setting | Value |
|---|---|
desiredCount |
1 |
deploymentConfiguration.minimumHealthyPercent |
0 |
deploymentConfiguration.maximumPercent |
100 |
| Deregistration delay | 15 s |
| Health check grace period | at least 60 s |
Each deploy has a short outage, from the old task’s deregistration until the new one passes its health check. A second task by mistake never turns healthy: it waits for the single-dispatcher lease the first one holds, and ECS keeps replacing it, which the service’s events show.
OIDC sign-in on AWS
Section titled “OIDC sign-in on AWS”Without the load balancer’s sign-in, the web app signs people in itself, and every rule of the
load balancer just forwards. Set ARCHMAX_SIGN_IN=oidc with the ARCHMAX_OIDC_* variables in
the task definition, and leave out every ARCHMAX_AWS_ALB_* one: a variable of the mode not
named refuses the start. Use a public app client (no client secret) with the authorization code grant and
PKCE, and allow both https://<host> and https://<host>/silent-callback.html as callback URLs:
the second is where the web app’s hidden check restores a session after a reload.
- Cognito as the issuer needs
ARCHMAX_OIDC_SIGN_OUT_URL: Cognito’s logout takesclient_idandlogout_urirather than the standard end-session parameters,https://<domain>.auth.<region>.amazoncognito.com/logout?client_id=<client-id>&logout_uri=https%3A%2F%2F<host>. Its access tokens carry no audience, so leaveARCHMAX_OIDC_AUDIENCEunset; the platform then checks the client id instead. - Auth0 as the issuer needs
ARCHMAX_OIDC_AUDIENCE, the identifier of the Auth0 API for the platform, or Auth0 issues access tokens nobody but Auth0 can read.
The environment variables reference lists both modes’ variables.