Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 43 additions & 40 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# UltiCode local development environment
#
# Do not copy this file and reuse the placeholders. Generate a private `.env`
# containing random infrastructure credentials and documented local app users:
# Credentials are deliberately empty in this tracked template. Generate a
# private `.env` containing random infrastructure and local app credentials:
#
# ./scripts/dev/init-env.sh
#
Expand All @@ -27,49 +27,49 @@ MANAGEMENT_PORT=9003
DB_HOST=localhost
DB_PORT=23306
DB_USER=ulticode
DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
DB_PASSWORD=
DB_NAME=ulticode
# Owner runtime schemas/accounts. Owners may share this MySQL instance, but
# each runtime account is scoped to its own schema.
AUTH_DB_HOST=localhost
AUTH_DB_PORT=23306
AUTH_DB_NAME=auth
AUTH_DB_USER=auth_rw
AUTH_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
AUTH_DB_PASSWORD=
ADMIN_DB_HOST=localhost
ADMIN_DB_PORT=23306
ADMIN_DB_NAME=admin
ADMIN_DB_USER=admin_rw
ADMIN_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
ADMIN_DB_PASSWORD=
APP_DB_HOST=localhost
APP_DB_PORT=23306
APP_DB_NAME=app
APP_DB_USER=app_rw
APP_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
APP_DB_PASSWORD=
NOTIFICATION_DB_HOST=localhost
NOTIFICATION_DB_PORT=23306
NOTIFICATION_DB_NAME=notification
NOTIFICATION_DB_USER=notification_rw
NOTIFICATION_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
NOTIFICATION_DB_PASSWORD=

# Submission owner runtime.
SUBMISSION_DB_HOST=localhost
SUBMISSION_DB_PORT=23306
SUBMISSION_DB_NAME=submission
SUBMISSION_DB_USER=submission_rw
SUBMISSION_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
SUBMISSION_DB_PASSWORD=
# Separate privileged identity for Submission owner migrations.
SUBMISSION_MIGRATION_DB_USER=migration_submission
SUBMISSION_MIGRATION_DB_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
SUBMISSION_MIGRATION_DB_PASSWORD=
APP_SUBMISSION_ROUTING_MODE=remote
# One-time cutover input (do not persist in `.env`):
# `SUBMISSION_CUTOVER_QUIESCE_CONFIRM=I_UNDERSTAND_SUBMISSION_QUIESCE_ALL_WRITERS`
SUBMISSION_CUTOVER_COMPLETE=false
SUBMISSION_APP_DB_USER=app_rw
SUBMISSION_APP_DB_HOST=%
NOTIFICATION_APP_DB_USER=
MYSQL_ROOT_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
DB_ROOT_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
MYSQL_ROOT_PASSWORD=
DB_ROOT_PASSWORD=
# Explicit privileged owner migration job input. Required when
# MIGRATION_SCHEMA is auth/admin/app/notification/submission; never reuse a
# runtime *_DB_USER as the migration account.
Expand All @@ -94,20 +94,22 @@ REDIS_ACL_DIR=.local/redis
# Per-owner Redis ACL credentials (review 2026-08-25). The shared
# REDIS_PASSWORD is gone: each security domain authenticates as its own ACL
# user in the runtime REDIS_ACL_DIR (default `.local/redis`; default user
# disabled). The values below are DEV-ONLY placeholders; init-env.sh generates
# the runtime ACL outside Git. Production must rotate all of them with
# scripts/runbooks/redis-acl-rotation.sh.
AUTH_REDIS_PASSWORD=dev-only-auth-redis-password
ADMIN_REDIS_PASSWORD=dev-only-admin-redis-password
APP_REDIS_PASSWORD=dev-only-app-redis-password
SUBMISSION_REDIS_PASSWORD=dev-only-submission-redis-password
SEARCH_REDIS_PASSWORD=dev-only-search-redis-password
NOTIFICATION_REDIS_PASSWORD=dev-only-notification-redis-password
JUDGE_REDIS_PASSWORD=dev-only-judge-redis-password
OPS_REDIS_PASSWORD=dev-only-ops-redis-password
HEALTH_REDIS_PASSWORD=dev-only-health-redis-password
REDIS_REPLICATION_PASSWORD=dev-only-redis-replication-password
REDIS_SENTINEL_PASSWORD=dev-only-redis-sentinel-password
# disabled). Leave all passwords empty in this tracked template. For local
# development, init-env.sh generates separate random passwords in the ignored
# .env file and renders the runtime ACL outside Git. Deployed environments must
# inject operator-managed secrets. Existing credentials are rotated separately
# with scripts/runbooks/redis-acl-rotation.sh; never reuse committed defaults.
AUTH_REDIS_PASSWORD=
ADMIN_REDIS_PASSWORD=
APP_REDIS_PASSWORD=
SUBMISSION_REDIS_PASSWORD=
SEARCH_REDIS_PASSWORD=
NOTIFICATION_REDIS_PASSWORD=
JUDGE_REDIS_PASSWORD=
OPS_REDIS_PASSWORD=
HEALTH_REDIS_PASSWORD=
REDIS_REPLICATION_PASSWORD=
REDIS_SENTINEL_PASSWORD=
# Production only: mount a secret-managed directory containing the certificate
# and private key; do not put PEM material in this repository.
# TLS_CERT_DIR=/run/secrets/ulticode-tls
Expand All @@ -125,7 +127,7 @@ REDIS_SENTINEL_PASSWORD=dev-only-redis-sentinel-password
# REDIS_USERNAME=ulticode-auth REDIS_PASSWORD="$AUTH_REDIS_PASSWORD"

# JWT and browser security
JWT_SECRET=GENERATED_BY_scripts_dev_init_env_sh_minimum_32_chars
JWT_SECRET=
JWT_COOKIE_SECURE=false
CORS_ALLOWED_ORIGINS=http://localhost:9002,http://localhost:9003,http://127.0.0.1:9002,http://127.0.0.1:9003
FRONTEND_URL=http://localhost:9002
Expand All @@ -139,23 +141,23 @@ NACOS_NAMESPACE=dev
NACOS_GROUP=DEFAULT_GROUP
DUBBO_NAMESPACE=dev
NACOS_USERNAME=ulticode_dev_admin
NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
NACOS_PASSWORD=
# Dev-only per-service Nacos registry identities generated by init-env.sh.
AUTH_NACOS_USERNAME=ulticode-auth
AUTH_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
AUTH_NACOS_PASSWORD=
ADMIN_NACOS_USERNAME=ulticode-admin
ADMIN_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
ADMIN_NACOS_PASSWORD=
APP_NACOS_USERNAME=ulticode-app
APP_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
APP_NACOS_PASSWORD=
SUBMISSION_NACOS_USERNAME=ulticode-submission
SUBMISSION_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
SUBMISSION_NACOS_PASSWORD=
NOTIFICATION_NACOS_USERNAME=ulticode-notification
NOTIFICATION_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
NOTIFICATION_NACOS_PASSWORD=
JUDGE_NACOS_USERNAME=ulticode-judge
JUDGE_NACOS_PASSWORD=GENERATED_BY_scripts_dev_init_env_sh
NACOS_AUTH_TOKEN=GENERATED_BY_scripts_dev_init_env_sh
NACOS_AUTH_IDENTITY_KEY=GENERATED_BY_scripts_dev_init_env_sh
NACOS_AUTH_IDENTITY_VALUE=GENERATED_BY_scripts_dev_init_env_sh
JUDGE_NACOS_PASSWORD=
NACOS_AUTH_TOKEN=
NACOS_AUTH_IDENTITY_KEY=
NACOS_AUTH_IDENTITY_VALUE=
# Production-only trust controls; set these in the deployment secret store.
#NACOS_DB_USER=nacos_rw
#NACOS_DB_PASSWORD=GENERATED_IN_DEPLOYMENT_SECRET_STORE
Expand Down Expand Up @@ -237,7 +239,7 @@ APP_STORAGE_S3_UPLOAD_TIMEOUT_MS=1800000
#MANAGED_OTLP_AUTHORIZATION=<secret-store>
# Generated by init-env.sh; startup/test fall back to an in-memory disposable
# value for older env files and never persist it automatically.
MEILI_MASTER_KEY=GENERATED_BY_scripts_dev_init_env_sh
MEILI_MASTER_KEY=
# Optional Judge0 async Adapter. Keep disabled for private/hidden tests.
# Enable only with an operator-managed HTTPS endpoint and secret-file API key.
#JUDGE0_ENABLED=false
Expand Down Expand Up @@ -330,11 +332,12 @@ APP_BOOTSTRAP_ADMIN_USERNAME=
APP_BOOTSTRAP_ADMIN_EMAIL=
APP_BOOTSTRAP_ADMIN_PASSWORD=

# Fixed credentials for the disposable local development database only.
# Per-checkout credentials for the disposable local development database only.
# init-env.sh generates the administrator password and matching browser input.
DEV_SEED_USERS_ENABLED=true
DEV_SEED_ADMIN_USERNAME=admin
DEV_SEED_ADMIN_EMAIL=admin@localhost.test
DEV_SEED_ADMIN_PASSWORD=admin123
DEV_SEED_ADMIN_PASSWORD=
DEV_SEED_ADMIN_ROLE=ADMIN

# Optional integrations are disabled until real local credentials are supplied.
Expand All @@ -355,4 +358,4 @@ EMAIL_FROM_NAME=UltiCode Dev
VITE_API_BASE_URL=/api
VITE_ADMIN_BASE_URL=http://localhost:9003
VITE_TEST_USERNAME=admin
VITE_TEST_PASSWORD=admin123
VITE_TEST_PASSWORD=
9 changes: 9 additions & 0 deletions .github/workflows/_backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,15 @@ jobs:
}
trap cleanup EXIT
cp .env.example "$env_file"
# Synthetic values for config-only validation; runtime templates stay empty.
printf '%s\n' \
"MYSQL_ROOT_PASSWORD=config-only-synthetic-mysql-root-password" \
"DB_PASSWORD=config-only-synthetic-db-password" \
"HEALTH_REDIS_PASSWORD=config-only-synthetic-health-redis-password" \
"NACOS_AUTH_TOKEN=config-only-synthetic-nacos-auth-token" \
"NACOS_AUTH_IDENTITY_KEY=config-only-synthetic-nacos-auth-identity-key" \
"NACOS_AUTH_IDENTITY_VALUE=config-only-synthetic-nacos-auth-identity-value" \
"MEILI_MASTER_KEY=config-only-synthetic-meili-master-key" >> "$env_file"
replication_password="$(openssl rand -hex 16)"
sentinel_password="$(openssl rand -hex 16)"
printf '\nNACOS_DB_USER=nacos_rw\nNACOS_DB_PASSWORD=ci-ha-config\nNACOS_SERVERS=nacos:8848 nacos-2:8848 nacos-3:8848\nREDIS_HA_CONFIG_DIR=%s\nREDIS_REPLICATION_PASSWORD=%s\nREDIS_SENTINEL_PASSWORD=%s\n' \
Expand Down
8 changes: 8 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ App 还单独读取现有 application child 的目录,以及已有 listener
cookies、源码、PM2 state 或原始服务日志。退出时仅停止该测试 project/home,保留本机卷供
诊断;GitHub hosted runner 生命周期负责回收其临时磁盘。

启动失败报告仅输出固定诊断词表、固定源码帧白名单及受限覆盖计数,不回显异常原文或 URL。每个 Owner 保留日志缺失、读取失败和尾部截断信息;信号为 false 只表示在受限日志尾部未观察到,不能据此认定服务未启动。Spring context 启动与投影 providers discovered 分别记录;历史成功与失败信号可以同时出现,不代表当前健康或因果关系。源码帧白名单随相关代码位置维护。

本机重现需在新 checkout 安装锁定依赖、PM2 与 Playwright Chromium,然后运行:

```bash
Expand Down Expand Up @@ -730,6 +732,8 @@ enabled-owner wiring 或业务 journey。

`.env` 是本地部署密管输入,由 `./scripts/dev/init-env.sh` 生成并被 Git 忽略;`.env.example` 与 `.env.test.example` 是可提交模板,不包含可用凭据。实现配置以各服务 `application.yml`、Compose 和 `ecosystem.config.cjs` 为准。

`.env.example` 的数据库、Redis ACL、Nacos、JWT、Meili 和开发用户凭据全部留空。首次本地初始化使用 `./scripts/dev/init-env.sh`,由初始化流程生成各角色独立的随机凭据并写入 ignored `.env`;开发管理员密码与 `VITE_TEST_PASSWORD` 使用同一份本次初始化值,验收入口继续从环境读取。启用开发管理员 bootstrap 且启动 Auth 时,`up.sh` 要求显式提供非空白 `DEV_SEED_ADMIN_PASSWORD`,缺失、空值或仅含 Java 空白字符的值会在基础设施变更前拒绝启动,不再回退到固定口令;`--skip-bootstrap`、`--prepare-submission-owner`、禁用开发用户或不启动 Auth 时不强求该密码。部署环境从密钥系统注入。不要直接使用模板启动,也不要重用曾提交到 Git 的默认密码。Redis ACL 生成器在任一角色密码缺失时拒绝生成配置;已部署凭据的撤销或轮换需要单独执行相应运维流程。 现有 `.env` 会原样保留,不自动更改或轮换已有密码。`init-env.sh --force` 会同时重新生成其他基础设施凭据,不能作为仅更改管理员口令的捷径。

### 常用变量

| 变量 | 用途 | 规则 |
Expand Down Expand Up @@ -831,3 +835,7 @@ Codex 前缀规则只能识别已列出的参数排列,无法覆盖任意脚
- [部署与回滚](OPERATIONS.md#部署发布与回滚)
- [Services 问题注册表](../services/docs/SERVICES_ISSUES.md)
- [Worker SLO Runbook](../services/docs/WORKER_SLO_RUNBOOK.md)

### 有界启动失败诊断

启动超时不放宽就绪门禁。失败报告记录最后一轮所选服务的固定 readiness 结果(HTTP 状态枚举或 PM2/banner 分类),而不是用历史启动成功行判断当前状态。额外依赖诊断只读取固定 IPv4 loopback 健康端点,限制 8 KiB 响应、每 socket 5 秒,并以 25 秒总时限和 2 秒强制终止上限约束整个子进程;不跟随跳转、不传认证、不输出原始响应或地址。只发布固定 db/redis/storage/problemOwnerRpc 的 up/down/unknown 枚举。额外探测发生在最终门禁轮次之后,不代表同一时刻;缺失探测或未选服务保留 unknown,不能当作健康或失败。日志 startup_signals 仍只表示有界日志尾部曾观察到的信号,成功与失败可同时存在。
Loading
Loading