0d236f57fb
Provide App Attest-bound, one-time onboarding AI access without creating accounts, with durable replay protection and production deployment safeguards.
111 lines
4.8 KiB
YAML
111 lines
4.8 KiB
YAML
services:
|
|
account-server:
|
|
image: ${ACCOUNT_IMAGE:-ghcr.io/hkgood/osg-account-server}:${IMAGE_TAG:-main}
|
|
pull_policy: always
|
|
restart: unless-stopped
|
|
init: true
|
|
user: "10001:10001"
|
|
stop_grace_period: 30s
|
|
environment:
|
|
APP_ENV: production
|
|
PORT: "8080"
|
|
PUBLIC_BASE_URL: https://account.osglab.com
|
|
INVITE_BASE_URL: https://osglab.com/i
|
|
APP_STORE_URL: ${APP_STORE_URL:?set the production App Store HTTPS URL}
|
|
|
|
# Reuse the existing MySQL; this stack intentionally creates no database.
|
|
DATABASE_URL: ${DATABASE_URL:?set DATABASE_URL for the existing MySQL}
|
|
DATABASE_USER: ${DATABASE_USER:?set DATABASE_USER}
|
|
DATABASE_PASSWORD: ${DATABASE_PASSWORD:?set DATABASE_PASSWORD}
|
|
DATABASE_POOL_SIZE: ${DATABASE_POOL_SIZE:-10}
|
|
DATABASE_MIGRATION_USER: ${DATABASE_MIGRATION_USER:?set DATABASE_MIGRATION_USER}
|
|
DATABASE_MIGRATION_PASSWORD: ${DATABASE_MIGRATION_PASSWORD:?set DATABASE_MIGRATION_PASSWORD}
|
|
|
|
JWT_ISSUER: https://account.osglab.com
|
|
JWT_AUDIENCE: ${JWT_AUDIENCE:-osgkeyboard-ios}
|
|
JWT_SECRET: ${JWT_SECRET:?set a random JWT secret}
|
|
ACCESS_TOKEN_MINUTES: ${ACCESS_TOKEN_MINUTES:-15}
|
|
REFRESH_TOKEN_DAYS: ${REFRESH_TOKEN_DAYS:-30}
|
|
GATEWAY_GRANT_DAYS: ${GATEWAY_GRANT_DAYS:-30}
|
|
FIELD_ENCRYPTION_KEY: ${FIELD_ENCRYPTION_KEY:?set a 32-byte Base64 key}
|
|
IDENTITY_HMAC_KEY: ${IDENTITY_HMAC_KEY:?set a distinct Base64 key}
|
|
IDENTITY_TOMBSTONE_RETENTION_DAYS: ${IDENTITY_TOMBSTONE_RETENTION_DAYS:-365}
|
|
|
|
ADMIN_ENABLED: ${ADMIN_ENABLED:-false}
|
|
ADMIN_MTLS_REQUIRED: ${ADMIN_MTLS_REQUIRED:-true}
|
|
ADMIN_BOOTSTRAP_ENABLED: ${ADMIN_BOOTSTRAP_ENABLED:-false}
|
|
ADMIN_BOOTSTRAP_OPERATOR_ID: ${ADMIN_BOOTSTRAP_OPERATOR_ID:-}
|
|
ADMIN_BOOTSTRAP_USERNAME: ${ADMIN_BOOTSTRAP_USERNAME:-}
|
|
ADMIN_BOOTSTRAP_PASSWORD_HASH: ${ADMIN_BOOTSTRAP_PASSWORD_HASH:-}
|
|
ADMIN_BOOTSTRAP_TOTP_SECRET_BASE32: ${ADMIN_BOOTSTRAP_TOTP_SECRET_BASE32:-}
|
|
ADMIN_SESSION_HOURS: ${ADMIN_SESSION_HOURS:-8}
|
|
ADMIN_MAXIMUM_MANUAL_GRANT: ${ADMIN_MAXIMUM_MANUAL_GRANT:-100000}
|
|
|
|
# The legacy key.osglab.com service remains independent and unchanged.
|
|
HINT_FEED_ENABLED: ${HINT_FEED_ENABLED:-false}
|
|
HINT_FEED_ZONE_ID: ${HINT_FEED_ZONE_ID:-UTC}
|
|
TOPHUB_API_KEY: ${TOPHUB_API_KEY:-}
|
|
|
|
APPLE_TEAM_ID: ${APPLE_TEAM_ID:?set Apple team ID}
|
|
APPLE_KEY_ID: ${APPLE_KEY_ID:?set Apple key ID}
|
|
APPLE_CLIENT_ID: ${APPLE_CLIENT_ID:-com.osgkeyboard.ios}
|
|
APPLE_PRIVATE_KEY_PEM: ${APPLE_PRIVATE_KEY_PEM:?set Apple private key PEM}
|
|
APPLE_INTEGRITY_ENVIRONMENT: production
|
|
APP_ATTEST_CHALLENGE_TTL_SECONDS: ${APP_ATTEST_CHALLENGE_TTL_SECONDS:-300}
|
|
ALLOW_DEVELOPMENT_APP_ATTEST: ${ALLOW_DEVELOPMENT_APP_ATTEST:-false}
|
|
ENFORCE_DEVICE_CHECK: "true"
|
|
ENFORCE_APP_ATTEST: "true"
|
|
|
|
VOLCENGINE_API_KEY: ${VOLCENGINE_API_KEY:?set Volcengine API key}
|
|
VOLCENGINE_RESOURCE_ID: ${VOLCENGINE_RESOURCE_ID:-volc.seedasr.sauc.duration}
|
|
VOLCENGINE_ASR_ENDPOINT: ${VOLCENGINE_ASR_ENDPOINT:-wss://openspeech.bytedance.com/api/v3/sauc/bigmodel}
|
|
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:?set DeepSeek API key}
|
|
DEEPSEEK_MODEL: ${DEEPSEEK_MODEL:-deepseek-v4-flash}
|
|
DEEPSEEK_REASONING_MODEL: ${DEEPSEEK_REASONING_MODEL:-}
|
|
DEEPSEEK_ENDPOINT: ${DEEPSEEK_ENDPOINT:-https://api.deepseek.com/v1}
|
|
|
|
SIGNUP_TRIAL_CREDITS: ${SIGNUP_TRIAL_CREDITS:-1000}
|
|
REFERRAL_INVITER_CREDITS: ${REFERRAL_INVITER_CREDITS:-1000}
|
|
REFERRAL_INVITEE_CREDITS: ${REFERRAL_INVITEE_CREDITS:-1000}
|
|
REFERRAL_BINDING_DAYS: ${REFERRAL_BINDING_DAYS:-7}
|
|
STOREKIT_ENABLED: ${STOREKIT_ENABLED:-true}
|
|
STOREKIT_BUNDLE_ID: ${STOREKIT_BUNDLE_ID:-com.osgkeyboard.ios}
|
|
STOREKIT_APP_APPLE_ID: ${STOREKIT_APP_APPLE_ID:-6781553267}
|
|
STOREKIT_PRODUCTS: ${STOREKIT_PRODUCTS:-500tks:500,1500tks:1500,3000tks:3000}
|
|
ports:
|
|
- "127.0.0.1:${ACCOUNT_BIND_PORT:-18080}:8080"
|
|
read_only: true
|
|
pids_limit: 256
|
|
tmpfs:
|
|
- /tmp:size=64m,mode=1777,noexec,nosuid,nodev
|
|
cap_drop:
|
|
- ALL
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/health/ready"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
start_period: 30s
|
|
retries: 3
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
networks:
|
|
- account-backend
|
|
# Separate egress keeps Apple/provider traffic available when the shared
|
|
# database network was created with Docker's --internal flag.
|
|
- account-egress
|
|
|
|
networks:
|
|
# Pre-create this shared network with:
|
|
# docker network create --internal account-backend
|
|
# Attach the existing MySQL container without publishing port 3306.
|
|
account-backend:
|
|
external: true
|
|
name: ${ACCOUNT_DOCKER_NETWORK:-account-backend}
|
|
account-egress:
|
|
driver: bridge
|