Skip to content

Distributed cache

Single-instance deployments cache GitHub access tokens in process memory. With multiple instances, each independently requests tokens from GitHub, increasing API usage and latency. A shared Valkey cache eliminates this redundancy by allowing all instances to read and write from the same token store.

Chinmina supports Valkey (or any Redis-compatible server) as a distributed cache backend. When configured, Chinmina uses a multi-level caching strategy: a fast local in-memory cache backed by the shared Valkey instance.

Values to save for configuration are marked in the instructions below like this: 📝 ENV_VAR_NAME.

Collect these values as you proceed through the infrastructure setup sections. They are used in the “Configure Chinmina” steps that follow each section.

  1. Create an ElastiCache Serverless Valkey cache (or replication group) by following the ElastiCache getting started guide. Place the cache in the same VPC as your Chinmina instances, or ensure network connectivity via VPC peering or security group rules.

    Save the cache endpoint as 📝 VALKEY_ADDRESS and cache name (replication group ID or serverless cache name) as 📝 VALKEY_IAM_CACHE_NAME.

  2. Enable in-transit encryption (TLS) on the cache. TLS is required for IAM authentication and is strongly recommended in all cases.

  3. Create an ElastiCache IAM-enabled user for Chinmina to authenticate with.

    Save the user ID as 📝 VALKEY_USERNAME.

  1. Set CACHE_TYPE=valkey and VALKEY_ADDRESS to the cache endpoint in host:port format:

    Terminal window
    CACHE_TYPE=valkey
    VALKEY_ADDRESS=chinmina-cache-abcdef.serverless.use1.cache.amazonaws.com:6379
  2. Enable IAM authentication and identify the cache:

    Terminal window
    VALKEY_IAM_ENABLED=true
    VALKEY_IAM_CACHE_NAME=chinmina-cache
    VALKEY_IAM_SERVERLESS=true

    Set VALKEY_IAM_SERVERLESS=true for ElastiCache Serverless caches, or false for replication groups.

  3. Set VALKEY_USERNAME to the IAM user ID created in the previous section:

    Terminal window
    VALKEY_USERNAME=chinmina-cache-user
  4. Grant the Chinmina execution role elasticache:Connect permission, scoped to the cache and user:

    example-elasticache-iam-policy.json
    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Effect": "Allow",
    "Action": "elasticache:Connect",
    "Resource": [
    "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:chinmina-cache",
    "arn:aws:elasticache:us-east-1:123456789012:user:chinmina-cache-user"
    ]
    }
    ]
    }

Cache encryption uses Tink for AEAD (Authenticated Encryption with Associated Data) with AES-256-GCM. An encrypted Tink keyset is stored in AWS Secrets Manager, protected by an AWS KMS envelope encryption key. At runtime, Chinmina decrypts the keyset and uses it to encrypt and decrypt cached tokens.

Create the KMS key and Secrets Manager secret

Section titled “Create the KMS key and Secrets Manager secret”
  1. Create a symmetric encryption KMS key (AES-256). Enable automatic annual rotation and set the deletion window to 30 days. Follow the KMS key creation guide for detailed instructions.

    Save the key ARN as 📝 CACHE_ENCRYPTION_KMS_ENVELOPE_KEY_URI.

  2. Create an alias for the key, for example alias/chinmina-cache-encryption. A key alias simplifies key rotation and policy management.

  3. Update the key policy to allow the Chinmina execution role to decrypt:

    example-kms-key-policy-statement.json
    {
    "Sid": "Allow Chinmina to decrypt the cache encryption keyset",
    "Effect": "Allow",
    "Principal": {
    "AWS": ["arn:aws:iam::123456789012:role/chinmina-task-role"]
    },
    "Action": ["kms:Decrypt", "kms:DescribeKey"],
    "Resource": "*"
    }
  4. Create a Secrets Manager secret to hold the encrypted Tink keyset. Use a naming convention such as /chinmina-bridge/{environment}/tink-keyset:

    Create the Secrets Manager secret
    aws secretsmanager create-secret \
    --name /chinmina-bridge/production/tink-keyset \
    --description "Encrypted Tink AEAD keyset for Chinmina cache encryption"

    Save the secret name as 📝 CACHE_ENCRYPTION_KEYSET_URI. The keyset content will be uploaded in the next section.

  5. Grant the Chinmina execution role access to read the secret:

    example-secrets-manager-iam-policy.json
    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Effect": "Allow",
    "Action": "secretsmanager:GetSecretValue",
    "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:/chinmina-bridge/*/tink-keyset-*"
    }
    ]
    }

    The trailing wildcard on the resource ARN accounts for the random suffix that Secrets Manager appends to secret names.

  1. Install the tinkey CLI by following the Tink installation instructions.

  2. Generate an AEAD keyset:

    Generate a new Tink keyset
    tinkey create-keyset \
    --key-template AES256_GCM \
    --out-format json \
    --out plaintext-keyset.json
  3. Encrypt the keyset with the KMS key:

    Encrypt the keyset
    tinkey encrypt-keyset \
    --in plaintext-keyset.json \
    --out encrypted-keyset.json \
    --master-key-uri aws-kms://arn:aws:kms:us-east-1:123456789012:key/abcd1234-a123-456a-a12b-a123b4cd56ef
  4. Delete the plaintext keyset:

    Delete the plaintext keyset
    shred -u plaintext-keyset.json
  5. Upload the encrypted keyset to the Secrets Manager secret:

    Upload the encrypted keyset
    aws secretsmanager put-secret-value \
    --secret-id /chinmina-bridge/production/tink-keyset \
    --secret-string file://encrypted-keyset.json

    Delete the local encrypted keyset file after uploading.

  1. Enable cache encryption:

    Terminal window
    CACHE_ENCRYPTION_ENABLED=true
  2. Set the Secrets Manager URI for the keyset. Use the aws-secretsmanager:// scheme followed by the secret name:

    Terminal window
    CACHE_ENCRYPTION_KEYSET_URI=aws-secretsmanager://chinmina-bridge/production/tink-keyset
  3. Set the KMS key URI for envelope decryption. Use the aws-kms:// scheme followed by the full key ARN:

    Terminal window
    CACHE_ENCRYPTION_KMS_ENVELOPE_KEY_URI=aws-kms://arn:aws:kms:us-east-1:123456789012:key/abcd1234-a123-456a-a12b-a123b4cd56ef
  4. On startup, Chinmina performs a test encrypt/decrypt cycle. If the keyset or KMS key is misconfigured, the service will fail to start with a clear error message.

Tink keysets hold multiple keys simultaneously. Only the primary key encrypts, but all keys decrypt. This allows zero-downtime rotation: add a new key, wait for all instances to load it, then promote to primary.

Chinmina refreshes its keyset from Secrets Manager every 15 minutes. The rotation procedure accounts for this interval and the cache TTL (tokens expire after 45 minutes).

T+0min Stage 1 — Upload keyset with new key (non-primary)
T+15min All instances can decrypt with both keys
T+75min Stage 2 — Promote new key to primary
T+90min All instances encrypting with new key
T+150min Stage 3 (optional) — Disable old key
  1. Download the current encrypted keyset from Secrets Manager:

    Download the current keyset
    aws secretsmanager get-secret-value \
    --secret-id /chinmina-bridge/production/tink-keyset \
    --query SecretString --output text > current-keyset.json
  2. Add a new key to the keyset. The add-key command adds a new key without promoting it to primary — the existing primary key continues to be used for encryption:

    Add a new key to the keyset
    tinkey add-key \
    --in current-keyset.json \
    --out stage1-keyset.json \
    --key-template AES256_GCM \
    --master-key-uri aws-kms://arn:aws:kms:REGION:ACCOUNT:key/KEY-ID
  3. Upload the updated keyset:

    Upload the keyset with the new key
    aws secretsmanager update-secret \
    --secret-id /chinmina-bridge/production/tink-keyset \
    --secret-string file://stage1-keyset.json
  4. Wait at least 15 minutes for all instances to refresh. After this point, all instances can decrypt with both keys.

Wait an additional 60 minutes after stage 1 completes (75 minutes from the start). This ensures all tokens encrypted with the old primary key have expired from the cache.

  1. Promote the new key to primary. Find the new key’s ID from the keyset metadata:

    Promote the new key to primary
    tinkey promote-key \
    --in stage1-keyset.json \
    --out stage2-keyset.json \
    --key-id <NEW_KEY_ID> \
    --master-key-uri aws-kms://arn:aws:kms:REGION:ACCOUNT:key/KEY-ID
  2. Upload the updated keyset:

    Upload the keyset with the promoted key
    aws secretsmanager update-secret \
    --secret-id /chinmina-bridge/production/tink-keyset \
    --secret-string file://stage2-keyset.json
  3. After another 15 minutes, all instances will encrypt new tokens with the new primary key.

After another cache TTL cycle (at least 60 minutes after stage 2), all tokens encrypted with the old key have expired. The old key can be safely disabled.

Disable the old key
tinkey disable-key \
--in stage2-keyset.json \
--out final-keyset.json \
--key-id <OLD_KEY_ID> \
--master-key-uri aws-kms://arn:aws:kms:REGION:ACCOUNT:key/KEY-ID

Upload the final keyset using the same aws secretsmanager update-secret command. Delete any local keyset files after uploading.