From 51cf7691beceb4d10b198229a045993774fb00ff Mon Sep 17 00:00:00 2001 From: "a.tolmachev" Date: Sat, 11 Apr 2026 12:40:00 +0300 Subject: [PATCH] config: simplify runtime environment model --- .env.example | 8 +++---- .github/workflows/deploy.yml | 24 ++++++++++----------- TASKS.md | 10 ++++----- apps/admin-api/Cargo.toml | 2 +- apps/admin-api/src/main.rs | 31 ++++++++++++++++++++++----- apps/mcp-server/Cargo.toml | 2 +- apps/mcp-server/src/main.rs | 30 ++++++++++++++++++++++---- apps/ui/scripts/playwright-stack.sh | 10 +++++---- crates/crank-registry/src/postgres.rs | 10 ++++++++- docker-compose.yml | 24 +++++++++++++-------- docs/demo-runbook.md | 2 +- docs/deployment.md | 17 ++++----------- docs/runtime-config.md | 21 ++++++++++++++---- docs/secrets-auth-plan.md | 2 +- 14 files changed, 127 insertions(+), 66 deletions(-) diff --git a/.env.example b/.env.example index e08f38f..a755f73 100644 --- a/.env.example +++ b/.env.example @@ -1,17 +1,16 @@ POSTGRES_DB=crank POSTGRES_USER=crank POSTGRES_PASSWORD=change-me -CRANK_DATABASE_URL=postgres://crank:change-me@postgres:5432/crank +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 CRANK_ADMIN_API_IMAGE=crank/admin-api:dev CRANK_MCP_SERVER_IMAGE=crank/mcp-server:dev CRANK_UI_IMAGE=crank/ui:dev CRANK_STORAGE_ROOT=/var/lib/crank/storage -CRANK_PUBLISH_HOST=127.0.0.1 CRANK_ADMIN_BIND=0.0.0.0:3001 CRANK_MCP_BIND=0.0.0.0:3002 CRANK_MCP_REFRESH_MS=5000 CRANK_LOG_LEVEL=info -CRANK_SECRET_PROVIDER=env CRANK_MASTER_KEY=change-me-master-key CRANK_SESSION_SECRET=change-me-session-secret CRANK_PASSWORD_PEPPER=change-me-password-pepper @@ -20,5 +19,4 @@ CRANK_BOOTSTRAP_ADMIN_EMAIL=owner@crank.local CRANK_BOOTSTRAP_ADMIN_PASSWORD=change-me-admin-password CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME=Crank Owner CRANK_DEMO_SEED=false -CRANK_PUBLIC_BASE_URL=https://crank.example.com -CRANK_MCP_PUBLIC_URL=https://crank.example.com/mcp +CRANK_BASE_URL=https://crank.example.com diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 24ccd99..5642fb7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -122,17 +122,15 @@ jobs: POSTGRES_DB: ${{ secrets.POSTGRES_DB }} POSTGRES_USER: ${{ secrets.POSTGRES_USER }} POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - CRANK_DATABASE_URL: ${{ secrets.CRANK_DATABASE_URL }} + POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }} + POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }} CRANK_STORAGE_ROOT: ${{ secrets.CRANK_STORAGE_ROOT }} - CRANK_PUBLISH_HOST: ${{ secrets.CRANK_PUBLISH_HOST }} CRANK_ADMIN_BIND: ${{ secrets.CRANK_ADMIN_BIND }} CRANK_MCP_BIND: ${{ secrets.CRANK_MCP_BIND }} CRANK_MCP_REFRESH_MS: ${{ secrets.CRANK_MCP_REFRESH_MS }} CRANK_LOG_LEVEL: ${{ secrets.CRANK_LOG_LEVEL }} - CRANK_SECRET_PROVIDER: ${{ secrets.CRANK_SECRET_PROVIDER }} CRANK_MASTER_KEY: ${{ secrets.CRANK_MASTER_KEY }} - CRANK_PUBLIC_BASE_URL: ${{ secrets.CRANK_PUBLIC_BASE_URL }} - CRANK_MCP_PUBLIC_URL: ${{ secrets.CRANK_MCP_PUBLIC_URL }} + CRANK_BASE_URL: ${{ secrets.CRANK_BASE_URL }} CRANK_SESSION_SECRET: ${{ secrets.CRANK_SESSION_SECRET }} CRANK_PASSWORD_PEPPER: ${{ secrets.CRANK_PASSWORD_PEPPER }} CRANK_SESSION_TTL_HOURS: ${{ secrets.CRANK_SESSION_TTL_HOURS }} @@ -151,17 +149,15 @@ jobs: append_if_set POSTGRES_DB "$POSTGRES_DB" append_if_set POSTGRES_USER "$POSTGRES_USER" append_if_set POSTGRES_PASSWORD "$POSTGRES_PASSWORD" - append_if_set CRANK_DATABASE_URL "$CRANK_DATABASE_URL" + append_if_set POSTGRES_HOST "$POSTGRES_HOST" + append_if_set POSTGRES_PORT "$POSTGRES_PORT" append_if_set CRANK_STORAGE_ROOT "$CRANK_STORAGE_ROOT" - append_if_set CRANK_PUBLISH_HOST "$CRANK_PUBLISH_HOST" append_if_set CRANK_ADMIN_BIND "$CRANK_ADMIN_BIND" append_if_set CRANK_MCP_BIND "$CRANK_MCP_BIND" append_if_set CRANK_MCP_REFRESH_MS "$CRANK_MCP_REFRESH_MS" append_if_set CRANK_LOG_LEVEL "$CRANK_LOG_LEVEL" - append_if_set CRANK_SECRET_PROVIDER "$CRANK_SECRET_PROVIDER" append_if_set CRANK_MASTER_KEY "$CRANK_MASTER_KEY" - append_if_set CRANK_PUBLIC_BASE_URL "$CRANK_PUBLIC_BASE_URL" - append_if_set CRANK_MCP_PUBLIC_URL "$CRANK_MCP_PUBLIC_URL" + append_if_set CRANK_BASE_URL "$CRANK_BASE_URL" append_if_set CRANK_SESSION_SECRET "$CRANK_SESSION_SECRET" append_if_set CRANK_PASSWORD_PEPPER "$CRANK_PASSWORD_PEPPER" append_if_set CRANK_SESSION_TTL_HOURS "$CRANK_SESSION_TTL_HOURS" @@ -184,13 +180,17 @@ jobs: set -e cd '${{ secrets.DEPLOY_PATH }}' required_vars=' - CRANK_DATABASE_URL + POSTGRES_HOST + POSTGRES_PORT + POSTGRES_DB + POSTGRES_USER + POSTGRES_PASSWORD CRANK_MASTER_KEY CRANK_SESSION_SECRET CRANK_PASSWORD_PEPPER CRANK_BOOTSTRAP_ADMIN_EMAIL CRANK_BOOTSTRAP_ADMIN_PASSWORD - CRANK_PUBLIC_BASE_URL + CRANK_BASE_URL ' for var in \$required_vars; do value=\$(grep -E \"^\${var}=\" .env | tail -n1 | cut -d= -f2- || true) diff --git a/TASKS.md b/TASKS.md index 9c8d1d0..28b1cb7 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,15 +2,15 @@ ## Current -### `feat/rust-docker-build-cache` +### `feat/runtime-env-simplification` Status: in_progress DoD: -- Rust Docker builds use a cache-friendly multi-stage layout -- `admin-api` and `mcp-server` reuse dependency layers across rebuilds -- noisy local artifacts are excluded from Docker build context -- deployment fails early when required secret-store env is missing +- runtime uses component-based PostgreSQL config as the primary model +- `CRANK_BASE_URL` replaces split public URL variables +- deployment uses one GitHub secret per runtime env variable +- deprecated env variables are removed from compose, examples, and docs ## Next diff --git a/apps/admin-api/Cargo.toml b/apps/admin-api/Cargo.toml index 77d5621..a292c9b 100644 --- a/apps/admin-api/Cargo.toml +++ b/apps/admin-api/Cargo.toml @@ -23,6 +23,7 @@ serde.workspace = true serde_json.workspace = true serde_yaml.workspace = true sha2.workspace = true +sqlx.workspace = true thiserror.workspace = true time.workspace = true tokio = { workspace = true, features = ["fs"] } @@ -33,4 +34,3 @@ uuid.workspace = true [dev-dependencies] reqwest.workspace = true serial_test = "3" -sqlx.workspace = true diff --git a/apps/admin-api/src/main.rs b/apps/admin-api/src/main.rs index 5a48f89..617b966 100644 --- a/apps/admin-api/src/main.rs +++ b/apps/admin-api/src/main.rs @@ -9,6 +9,7 @@ mod storage; use std::{env, net::SocketAddr, path::PathBuf}; use crank_registry::PostgresRegistry; +use sqlx::postgres::PgConnectOptions; use tokio::net::TcpListener; use tracing::info; @@ -28,15 +29,13 @@ async fn main() -> Result<(), Box> { ) .init(); - let database_url = env::var("CRANK_DATABASE_URL")?; let storage_root = PathBuf::from( env::var("CRANK_STORAGE_ROOT").unwrap_or_else(|_| "/var/lib/crank/storage".into()), ); let bind_addr = env::var("CRANK_ADMIN_BIND").unwrap_or_else(|_| "0.0.0.0:3001".into()); - let public_base_url = - env::var("CRANK_PUBLIC_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".into()); + let base_url = env::var("CRANK_BASE_URL").unwrap_or_else(|_| "http://localhost:3000".into()); let socket_addr: SocketAddr = bind_addr.parse()?; - let registry = PostgresRegistry::connect(&database_url).await?; + let registry = PostgresRegistry::connect_with_options(database_options_from_env()?).await?; let auth_settings = AuthSettings { session_secret: env::var("CRANK_SESSION_SECRET")?, password_pepper: env::var("CRANK_PASSWORD_PEPPER")?, @@ -44,7 +43,7 @@ async fn main() -> Result<(), Box> { .ok() .and_then(|value| value.parse::().ok()) .unwrap_or(24), - cookie_secure: public_base_url.starts_with("https://"), + cookie_secure: base_url.starts_with("https://"), bootstrap_admin: BootstrapAdminConfig { email: env::var("CRANK_BOOTSTRAP_ADMIN_EMAIL")?, password: env::var("CRANK_BOOTSTRAP_ADMIN_PASSWORD")?, @@ -79,3 +78,25 @@ fn env_flag(name: &str) -> bool { Some("1" | "true" | "yes" | "on") ) } + +fn database_options_from_env() -> Result> { + if let Ok(database_url) = env::var("CRANK_DATABASE_URL") { + return Ok(database_url.parse::()?); + } + + let host = env::var("POSTGRES_HOST").unwrap_or_else(|_| "postgres".into()); + let port = env::var("POSTGRES_PORT") + .ok() + .and_then(|value| value.parse::().ok()) + .unwrap_or(5432); + let database = env::var("POSTGRES_DB").unwrap_or_else(|_| "crank".into()); + let username = env::var("POSTGRES_USER").unwrap_or_else(|_| "crank".into()); + let password = env::var("POSTGRES_PASSWORD").unwrap_or_else(|_| "crank".into()); + + Ok(PgConnectOptions::new() + .host(&host) + .port(port) + .database(&database) + .username(&username) + .password(&password)) +} diff --git a/apps/mcp-server/Cargo.toml b/apps/mcp-server/Cargo.toml index 3dfe7f1..22f2140 100644 --- a/apps/mcp-server/Cargo.toml +++ b/apps/mcp-server/Cargo.toml @@ -16,6 +16,7 @@ futures-util = "0.3" serde.workspace = true serde_json.workspace = true sha2.workspace = true +sqlx.workspace = true time.workspace = true tokio = { workspace = true, features = ["sync"] } tracing.workspace = true @@ -27,4 +28,3 @@ crank-adapter-grpc = { path = "../../crates/crank-adapter-grpc", features = ["te crank-mapping = { path = "../../crates/crank-mapping" } crank-schema = { path = "../../crates/crank-schema" } reqwest.workspace = true -sqlx.workspace = true diff --git a/apps/mcp-server/src/main.rs b/apps/mcp-server/src/main.rs index 330419c..02019c9 100644 --- a/apps/mcp-server/src/main.rs +++ b/apps/mcp-server/src/main.rs @@ -7,6 +7,7 @@ use std::{env, net::SocketAddr, time::Duration}; use crank_registry::PostgresRegistry; use crank_runtime::SecretCrypto; +use sqlx::postgres::PgConnectOptions; use tokio::net::TcpListener; use tracing::info; @@ -21,18 +22,17 @@ async fn main() -> Result<(), Box> { ) .init(); - let database_url = env::var("CRANK_DATABASE_URL")?; let bind_addr = env::var("CRANK_MCP_BIND").unwrap_or_else(|_| "0.0.0.0:3002".into()); - let public_base_url = env::var("CRANK_PUBLIC_BASE_URL").ok(); + let base_url = env::var("CRANK_BASE_URL").ok(); let refresh_interval = env::var("CRANK_MCP_REFRESH_MS") .ok() .and_then(|value| value.parse::().ok()) .map(Duration::from_millis) .unwrap_or_else(|| Duration::from_secs(5)); let socket_addr: SocketAddr = bind_addr.parse()?; - let registry = PostgresRegistry::connect(&database_url).await?; + let registry = PostgresRegistry::connect_with_options(database_options_from_env()?).await?; let secret_crypto = SecretCrypto::new(&env::var("CRANK_MASTER_KEY")?)?; - let app = build_app(registry, refresh_interval, public_base_url, secret_crypto); + let app = build_app(registry, refresh_interval, base_url, secret_crypto); let listener = TcpListener::bind(socket_addr).await?; info!("mcp-server listening on {}", socket_addr); @@ -42,6 +42,28 @@ async fn main() -> Result<(), Box> { Ok(()) } +fn database_options_from_env() -> Result> { + if let Ok(database_url) = env::var("CRANK_DATABASE_URL") { + return Ok(database_url.parse::()?); + } + + let host = env::var("POSTGRES_HOST").unwrap_or_else(|_| "postgres".into()); + let port = env::var("POSTGRES_PORT") + .ok() + .and_then(|value| value.parse::().ok()) + .unwrap_or(5432); + let database = env::var("POSTGRES_DB").unwrap_or_else(|_| "crank".into()); + let username = env::var("POSTGRES_USER").unwrap_or_else(|_| "crank".into()); + let password = env::var("POSTGRES_PASSWORD").unwrap_or_else(|_| "crank".into()); + + Ok(PgConnectOptions::new() + .host(&host) + .port(port) + .database(&database) + .username(&username) + .password(&password)) +} + #[cfg(test)] mod tests { use std::{ diff --git a/apps/ui/scripts/playwright-stack.sh b/apps/ui/scripts/playwright-stack.sh index 30e2a98..24dcc97 100644 --- a/apps/ui/scripts/playwright-stack.sh +++ b/apps/ui/scripts/playwright-stack.sh @@ -91,13 +91,16 @@ until docker exec "$POSTGRES_CONTAINER" pg_isready -U "$POSTGRES_USER" -d "$POST sleep 1 done -export CRANK_DATABASE_URL="postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@127.0.0.1:$POSTGRES_PORT/$POSTGRES_DB" +export POSTGRES_HOST="127.0.0.1" +export POSTGRES_PORT="$POSTGRES_PORT" +export POSTGRES_DB="$POSTGRES_DB" +export POSTGRES_USER="$POSTGRES_USER" +export POSTGRES_PASSWORD="$POSTGRES_PASSWORD" export CRANK_STORAGE_ROOT="$TMP_DIR/storage" export CRANK_ADMIN_BIND="0.0.0.0:$ADMIN_PORT" export CRANK_MCP_BIND="0.0.0.0:$MCP_PORT" export CRANK_MCP_REFRESH_MS="1000" export CRANK_LOG_LEVEL="info" -export CRANK_SECRET_PROVIDER="env" export CRANK_SESSION_SECRET="e2e-session-secret" export CRANK_PASSWORD_PEPPER="e2e-password-pepper" export CRANK_MASTER_KEY="0000000000000000000000000000000000000000000000000000000000000000" @@ -106,8 +109,7 @@ export CRANK_BOOTSTRAP_ADMIN_EMAIL="$ADMIN_EMAIL" export CRANK_BOOTSTRAP_ADMIN_PASSWORD="$ADMIN_PASSWORD" export CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME="Crank E2E" export CRANK_DEMO_SEED="true" -export CRANK_PUBLIC_BASE_URL="http://127.0.0.1:$UI_PORT" -export CRANK_MCP_PUBLIC_URL="http://127.0.0.1:$MCP_PORT" +export CRANK_BASE_URL="http://127.0.0.1:$UI_PORT" export CRANK_E2E_STREAM_FIXTURE_PORT="$STREAM_FIXTURE_PORT" export CRANK_E2E_GRPC_FIXTURE_BIND="$GRPC_FIXTURE_BIND" diff --git a/crates/crank-registry/src/postgres.rs b/crates/crank-registry/src/postgres.rs index 60fb54b..fe6fbce 100644 --- a/crates/crank-registry/src/postgres.rs +++ b/crates/crank-registry/src/postgres.rs @@ -10,7 +10,7 @@ use serde::{Serialize, de::DeserializeOwned}; use serde_json::Value; use sqlx::{ PgPool, Postgres, Row, Transaction, - postgres::{PgPoolOptions, PgRow}, + postgres::{PgConnectOptions, PgPoolOptions, PgRow}, types::Json, }; @@ -46,6 +46,14 @@ impl PostgresRegistry { Self::connect_in_schema(database_url, None).await } + pub async fn connect_with_options( + connect_options: PgConnectOptions, + ) -> Result { + let pool = PgPoolOptions::new().connect_with(connect_options).await?; + migrations::apply_postgres(&pool).await?; + Ok(Self { pool }) + } + pub fn pool(&self) -> &PgPool { &self.pool } diff --git a/docker-compose.yml b/docker-compose.yml index 85b6a75..9de452e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,13 +23,16 @@ services: dockerfile: apps/admin-api/Dockerfile restart: unless-stopped environment: - CRANK_DATABASE_URL: ${CRANK_DATABASE_URL:-postgres://crank:crank@postgres:5432/crank} + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} + POSTGRES_DB: ${POSTGRES_DB:-crank} + POSTGRES_USER: ${POSTGRES_USER:-crank} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} CRANK_ADMIN_BIND: ${CRANK_ADMIN_BIND:-0.0.0.0:3001} CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-info} - CRANK_SECRET_PROVIDER: ${CRANK_SECRET_PROVIDER:-env} CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} - CRANK_PUBLIC_BASE_URL: ${CRANK_PUBLIC_BASE_URL:-http://localhost:3000} + CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} CRANK_SESSION_SECRET: ${CRANK_SESSION_SECRET} CRANK_PASSWORD_PEPPER: ${CRANK_PASSWORD_PEPPER} CRANK_SESSION_TTL_HOURS: ${CRANK_SESSION_TTL_HOURS:-24} @@ -43,7 +46,7 @@ services: volumes: - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} ports: - - "${CRANK_PUBLISH_HOST:-127.0.0.1}:3001:3001" + - "127.0.0.1:3001:3001" healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:3001/health"] interval: 15s @@ -57,21 +60,24 @@ services: dockerfile: apps/mcp-server/Dockerfile restart: unless-stopped environment: - CRANK_DATABASE_URL: ${CRANK_DATABASE_URL:-postgres://crank:crank@postgres:5432/crank} + POSTGRES_HOST: ${POSTGRES_HOST:-postgres} + POSTGRES_PORT: ${POSTGRES_PORT:-5432} + POSTGRES_DB: ${POSTGRES_DB:-crank} + POSTGRES_USER: ${POSTGRES_USER:-crank} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} CRANK_MCP_BIND: ${CRANK_MCP_BIND:-0.0.0.0:3002} CRANK_MCP_REFRESH_MS: ${CRANK_MCP_REFRESH_MS:-5000} CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-info} - CRANK_SECRET_PROVIDER: ${CRANK_SECRET_PROVIDER:-env} CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} - CRANK_PUBLIC_BASE_URL: ${CRANK_PUBLIC_BASE_URL:-http://localhost:3000} + CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} depends_on: postgres: condition: service_healthy volumes: - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} ports: - - "${CRANK_PUBLISH_HOST:-127.0.0.1}:3002:3002" + - "127.0.0.1:3002:3002" healthcheck: test: ["CMD", "curl", "--fail", "http://127.0.0.1:3002/health"] interval: 15s @@ -85,7 +91,7 @@ services: dockerfile: apps/ui/Dockerfile restart: unless-stopped ports: - - "${CRANK_PUBLISH_HOST:-127.0.0.1}:3000:3000" + - "127.0.0.1:3000:3000" healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] interval: 15s diff --git a/docs/demo-runbook.md b/docs/demo-runbook.md index 21d67fb..30197b7 100644 --- a/docs/demo-runbook.md +++ b/docs/demo-runbook.md @@ -7,7 +7,7 @@ ## 2. Предусловия - доступен `PostgreSQL`; -- задан `CRANK_DATABASE_URL`; +- заданы `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`; - задан `CRANK_STORAGE_ROOT`; - доступны `CRANK_ADMIN_BIND` и `CRANK_MCP_BIND`; - если нужен заполненный demo state без ручного онбординга, задан `CRANK_DEMO_SEED=true`; diff --git a/docs/deployment.md b/docs/deployment.md index 5416aa0..c275084 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -165,17 +165,10 @@ services: - поднимать `postgres`, `admin-api`, `mcp-server`, `ui`; - использовать env vars вместо hardcoded config; - иметь persistent storage для БД и artifacts; -- поддерживать настраиваемый bind host для published ports; -- публиковать `ui`, `admin-api` и `mcp-server` на loopback, если reverse proxy живет на том же хосте; -- публиковать `ui`, `admin-api` и `mcp-server` на LAN bind address или `0.0.0.0`, если reverse proxy вынесен на отдельный хост; +- публиковать `ui`, `admin-api` и `mcp-server` на loopback по умолчанию; - не публиковать внутренние сервисы наружу без необходимости; - содержать healthchecks. -Практически это должно быть управляемо через env вроде `CRANK_PUBLISH_HOST`: - -- `127.0.0.1` для local reverse proxy на том же Linux host; -- конкретный внутренний IP или `0.0.0.0` для отдельного центрального reverse proxy в LAN. - ## 10. Healthchecks Нужны как минимум: @@ -238,20 +231,18 @@ CD для MVP должен: Workflow собирает `.env` на сервере из отдельных GitHub secrets. Для runtime-переменных нужно создавать secrets с теми же именами, что и у приложений: +- `POSTGRES_HOST` +- `POSTGRES_PORT` - `POSTGRES_DB` - `POSTGRES_USER` - `POSTGRES_PASSWORD` -- `CRANK_DATABASE_URL` - `CRANK_STORAGE_ROOT` -- `CRANK_PUBLISH_HOST` - `CRANK_ADMIN_BIND` - `CRANK_MCP_BIND` - `CRANK_MCP_REFRESH_MS` - `CRANK_LOG_LEVEL` -- `CRANK_SECRET_PROVIDER` - `CRANK_MASTER_KEY` -- `CRANK_PUBLIC_BASE_URL` -- `CRANK_MCP_PUBLIC_URL` +- `CRANK_BASE_URL` - `CRANK_SESSION_SECRET` - `CRANK_PASSWORD_PEPPER` - `CRANK_SESSION_TTL_HOURS` diff --git a/docs/runtime-config.md b/docs/runtime-config.md index d67d4c7..4aa1795 100644 --- a/docs/runtime-config.md +++ b/docs/runtime-config.md @@ -55,7 +55,11 @@ var/crank/ Минимально ожидаются: -- `CRANK_DATABASE_URL` +- `POSTGRES_HOST` +- `POSTGRES_PORT` +- `POSTGRES_DB` +- `POSTGRES_USER` +- `POSTGRES_PASSWORD` - `CRANK_ADMIN_API_IMAGE` - `CRANK_MCP_SERVER_IMAGE` - `CRANK_UI_IMAGE` @@ -64,10 +68,8 @@ var/crank/ - `CRANK_MCP_BIND` - `CRANK_MCP_REFRESH_MS` - `CRANK_LOG_LEVEL` -- `CRANK_SECRET_PROVIDER` - `CRANK_MASTER_KEY` -- `CRANK_PUBLIC_BASE_URL` -- `CRANK_MCP_PUBLIC_URL` +- `CRANK_BASE_URL` Опционально: @@ -151,6 +153,17 @@ runtime env-переменной. Это значит, что: - `CRANK_DEMO_SEED` хранится как GitHub secret `CRANK_DEMO_SEED`; - и так же для остальных runtime-переменных. +Для БД основной runtime-контракт теперь компонентный: + +- `POSTGRES_HOST` +- `POSTGRES_PORT` +- `POSTGRES_DB` +- `POSTGRES_USER` +- `POSTGRES_PASSWORD` + +`CRANK_DATABASE_URL` допускается только как backward-compatible fallback для локальных тестов и +переходного периода, но не как основная deployment-модель. + ## 8.1. Delivery artifacts Для production-like запуска проект должен поставляться с: diff --git a/docs/secrets-auth-plan.md b/docs/secrets-auth-plan.md index 7e071c5..bbf21fb 100644 --- a/docs/secrets-auth-plan.md +++ b/docs/secrets-auth-plan.md @@ -14,7 +14,7 @@ - `auth_profile_ref` в `ExecutionConfig`; - CRUD endpoints для `auth_profiles`; - UI поля для upstream auth headers; -- env `CRANK_SECRET_PROVIDER` и `CRANK_MASTER_KEY` в runtime docs. +- env `CRANK_MASTER_KEY` в runtime docs. Что еще не работает end-to-end: