Merge pull request 'Six modules take their secrets from files; the rest say precisely why not (issue 041)' (#32) from feat/migration-blockers into main
This commit was merged in pull request #32.
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
"type": "file",
|
||||
"path": "/var/lib/amqp-email-forwarder/app.env",
|
||||
"mode": "0600",
|
||||
"content": "AMQP_HOST=${bound:amqp:at}\nAMQP_PORT=${bound:amqp:port}\nAMQP_USER=${bound:amqp:as}\nAMQP_PASSWORD=${secret:amqp}\nAMQP_VHOST=EMAILDELIVERY_T\nAMQP_EXCHANGE=News.TransactionalEmailing.Command\nAMQP_QUEUE=email-forwarder\nAMQP_URL=amqp://${bound:amqp:as}:${secret:amqp}@${bound:amqp:at}:${bound:amqp:port}/EMAILDELIVERY_T\nSMTP_HOST=mail.novox.be\nSMTP_PORT=587\nSMTP_USER=${secret:smtp-user}\nSMTP_PASSWORD=${secret:smtp-password}\n"
|
||||
"content": "AMQP_HOST=${bound:amqp:at}\nAMQP_PORT=${bound:amqp:port}\nAMQP_USER=${bound:amqp:as}\nAMQP_VHOST=EMAILDELIVERY_T\nAMQP_EXCHANGE=News.TransactionalEmailing.Command\nAMQP_QUEUE=email-forwarder\nAMQP_URL=amqp://${bound:amqp:as}:${secret:amqp}@${bound:amqp:at}:${bound:amqp:port}/EMAILDELIVERY_T\nSMTP_HOST=mail.novox.be\nSMTP_PORT=587\nSMTP_USER=${secret:smtp-user}\nSMTP_PASSWORD=${secret:smtp-password}\n"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
@@ -50,7 +50,7 @@
|
||||
"restart-on": [
|
||||
"app-env"
|
||||
],
|
||||
"secrets-in-environment": "the runtime reads its SMTP and AMQP settings from the environment; a file twin in the SDK is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the application's own code reads AMQP_URL, SMTP_USER and SMTP_PASSWORD from the environment (amqp-email-forwarder app.js); converting is that repository's change"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"type": "file",
|
||||
"path": "/var/lib/amqp-ping/amqp.env",
|
||||
"mode": "0600",
|
||||
"content": "MESH_AMQP_HOST=${bound:amqp:at}\nMESH_AMQP_PORT=${bound:amqp:port}\nMESH_AMQP_USER=${bound:amqp:as}\nMESH_AMQP_VHOST=${bound:amqp:as}\nMESH_AMQP_PASSWORD=${secret:amqp}\n"
|
||||
"content": "MESH_AMQP_HOST=${bound:amqp:at}\nMESH_AMQP_PORT=${bound:amqp:port}\nMESH_AMQP_USER=${bound:amqp:as}\nMESH_AMQP_VHOST=${bound:amqp:as}\n"
|
||||
},
|
||||
{
|
||||
"id": "net",
|
||||
@@ -49,10 +49,12 @@
|
||||
"name": "amqp-ping",
|
||||
"network": "amqp-ping",
|
||||
"volumes": [
|
||||
"/var/lib/mesh/amqp-ping/broker:/run/secrets/broker:ro"
|
||||
"/var/lib/mesh/amqp-ping/broker:/run/secrets/broker:ro",
|
||||
"/var/lib/amqp-ping/amqp.secret:/run/secrets/amqp:ro"
|
||||
],
|
||||
"env": {
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker"
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
||||
"MESH_AMQP_PASSWORD_FILE": "/run/secrets/amqp"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/amqp-ping/amqp.env"
|
||||
@@ -60,8 +62,7 @@
|
||||
"restart-on": [
|
||||
"amqp-env"
|
||||
],
|
||||
"artifact": "runtime",
|
||||
"secrets-in-environment": "the runtime reads MESH_AMQP_* from the environment; a file twin in the SDK is the per-module work of issue 041"
|
||||
"artifact": "runtime"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
"volumes": [
|
||||
"/services/baserow/data:/baserow/data"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "baserow reads DATABASE_PASSWORD, REDIS_PASSWORD and SECRET_KEY with os.getenv and has no _FILE twin (settings/base.py); not convertible"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"ports": [
|
||||
"35621:35621"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the application's own code reads SMTP_AUTH_USER/PASS from the environment (de-spiegel server/index.js); converting is that repository's change"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
"volumes": [
|
||||
"/services/gitea/gitea:/data"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "gitea honours GITEA__database__PASSWD__FILE and GITEA__security__INTERNAL_TOKEN__FILE; convertible, awaiting a bed that proves it"
|
||||
},
|
||||
{
|
||||
"id": "admin-bootstrap",
|
||||
@@ -139,7 +139,7 @@
|
||||
"-c",
|
||||
"su-exec git gitea admin user create --admin --username \"$MESH_GITEA_ADMIN_USER\" --email mesh-admin@localhost --password \"$(cat /run/secrets/admin)\" --must-change-password=false || true"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "gitea honours GITEA__database__PASSWD__FILE and GITEA__security__INTERNAL_TOKEN__FILE; convertible, awaiting a bed that proves it"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
+11
-11
@@ -32,13 +32,6 @@
|
||||
"path": "/var/lib/grafana-module",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/grafana-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "GF_SECURITY_ADMIN_PASSWORD=${secret:admin}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
@@ -46,21 +39,28 @@
|
||||
"mode": "0700",
|
||||
"owner": "472:472"
|
||||
},
|
||||
{
|
||||
"id": "server-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/grafana-module/server.env",
|
||||
"mode": "0600",
|
||||
"content": "GF_SECURITY_ADMIN_PASSWORD=${secret:admin}\n"
|
||||
},
|
||||
{
|
||||
"id": "server",
|
||||
"type": "container",
|
||||
"name": "grafana",
|
||||
"image": "grafana/grafana@sha256:f772d434e8fab0049deb2b1b30abd43342bcfca1537614aa8d36080232cf4283",
|
||||
"env-file": [
|
||||
"/var/lib/grafana-module/server.env"
|
||||
],
|
||||
"ports": [
|
||||
"3000"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/grafana/data:/var/lib/grafana"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"env-file": [
|
||||
"/var/lib/grafana-module/server.env"
|
||||
],
|
||||
"secrets-in-environment": "grafana honours GF_SECURITY_ADMIN_PASSWORD__FILE; convertible, awaiting a bed that exercises the admin password (assigned-grafana serves tools only)"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"ports": [
|
||||
"8000"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the image seds ICECAST_*_PASSWORD into icecast.xml and has no _FILE; convertible by mounting a generated icecast.xml, not yet done"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"/services/influxdb/data:/var/lib/influxdb2",
|
||||
"/services/influxdb/config:/etc/influxdb2"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the image honours DOCKER_INFLUXDB_INIT_PASSWORD_FILE and _ADMIN_TOKEN_FILE; convertible, awaiting a bed that proves it"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
"ports": [
|
||||
"9000"
|
||||
],
|
||||
"secrets-in-environment": "the API reads its settings from the environment; converting is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the application's own code reads MONGO_URL and MINIO_SECRET from the environment (invoicing-app server/src/config.js); converting is that repository's change"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
"ports": [
|
||||
"8080"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "KC_DB_PASSWORD is convertible through a generated keycloak.conf (db-password=); KEYCLOAK_ADMIN_PASSWORD is env-only before Keycloak 26; not yet converted"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
"ports": [
|
||||
"8283"
|
||||
],
|
||||
"secrets-in-environment": "the runtime reads its settings from the environment; converting is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the letta image is env-driven and its file-source support could not be verified; the mesh runtime can take its password from config.json (client.ts) \u2014 not yet converted"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
@@ -105,7 +105,7 @@
|
||||
"runtime-config"
|
||||
],
|
||||
"artifact": "runtime",
|
||||
"secrets-in-environment": "the runtime reads its settings from the environment; converting is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the letta image is env-driven and its file-source support could not be verified; the mesh runtime can take its password from config.json (client.ts) \u2014 not yet converted"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
|
||||
+14
-22
@@ -218,7 +218,7 @@
|
||||
"/var/lib/mailu/mailu.env",
|
||||
"/var/lib/mailu/secret.env"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "redis",
|
||||
@@ -246,7 +246,7 @@
|
||||
"/services/mailu/data/data:/data",
|
||||
"/services/mailu/data/dkim:/dkim"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "imap",
|
||||
@@ -255,14 +255,12 @@
|
||||
"image": "ghcr.io/mailu/dovecot@sha256:46d18ba51032be8ebd6841aa49c1ef8762c729038c5fd86a081b5b884d478af9",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/mailu.env",
|
||||
"/var/lib/mailu/secret.env"
|
||||
"/var/lib/mailu/mailu.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/mail:/mail",
|
||||
"/services/mailu/data/overrides/dovecot:/overrides:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "smtp",
|
||||
@@ -271,14 +269,12 @@
|
||||
"image": "ghcr.io/mailu/postfix@sha256:bbf882880f68849511710b35237a933f3fe80c4b28bf48ff20205dbd1f1433d7",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/mailu.env",
|
||||
"/var/lib/mailu/secret.env"
|
||||
"/var/lib/mailu/mailu.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/mailqueue:/queue",
|
||||
"/services/mailu/data/overrides/postfix:/overrides:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "antispam",
|
||||
@@ -287,14 +283,12 @@
|
||||
"image": "ghcr.io/mailu/rspamd@sha256:e87ab93dd252cc69499caa5317dd10d445fd4291a7ecf6bca09793c7d475a0c8",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/mailu.env",
|
||||
"/var/lib/mailu/secret.env"
|
||||
"/var/lib/mailu/mailu.env"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mailu/data/filter:/var/lib/rspamd",
|
||||
"/services/mailu/data/overrides/rspamd:/etc/rspamd/override.d:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "antivirus",
|
||||
@@ -309,7 +303,7 @@
|
||||
"volumes": [
|
||||
"/services/mailu/data/filter:/data"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "webmail",
|
||||
@@ -325,7 +319,7 @@
|
||||
"/services/mailu/data/webmail:/data",
|
||||
"/services/mailu/data/overrides/roundcube:/overrides:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "webdav",
|
||||
@@ -340,7 +334,7 @@
|
||||
"volumes": [
|
||||
"/services/mailu/data/dav:/data"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "fetchmail",
|
||||
@@ -355,7 +349,7 @@
|
||||
"volumes": [
|
||||
"/services/mailu/data/data/fetchmail:/data"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
||||
},
|
||||
{
|
||||
"id": "front",
|
||||
@@ -364,8 +358,7 @@
|
||||
"image": "ghcr.io/mailu/nginx@sha256:09f28ab6d36367fcacc7994f7021f132ac845bdc05f04bf80906102d11aaa057",
|
||||
"network": "mailu",
|
||||
"env-file": [
|
||||
"/var/lib/mailu/mailu.env",
|
||||
"/var/lib/mailu/secret.env"
|
||||
"/var/lib/mailu/mailu.env"
|
||||
],
|
||||
"ports": [
|
||||
"25",
|
||||
@@ -377,8 +370,7 @@
|
||||
"volumes": [
|
||||
"/services/mailu/data/certs:/certs",
|
||||
"/services/mailu/data/overrides/nginx:/overrides:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -50,11 +50,11 @@
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "db-env",
|
||||
"id": "database-url",
|
||||
"type": "file",
|
||||
"path": "/var/lib/mesh-catalog/db.env",
|
||||
"path": "/var/lib/mesh-catalog/database.url",
|
||||
"mode": "0600",
|
||||
"content": "DATABASE_URL=postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\n"
|
||||
"content": "postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\n"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
@@ -63,19 +63,17 @@
|
||||
"network": "host",
|
||||
"volumes": [
|
||||
"/var/lib/mesh/mesh-catalog/broker:/run/secrets/broker:ro",
|
||||
"/var/lib/mesh-catalog:/run/state"
|
||||
"/var/lib/mesh-catalog:/run/state",
|
||||
"/var/lib/mesh-catalog/database.url:/run/secrets/database-url:ro"
|
||||
],
|
||||
"env": {
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker"
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
||||
"DATABASE_URL_FILE": "/run/secrets/database-url"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/mesh-catalog/db.env"
|
||||
],
|
||||
"artifact": "runtime",
|
||||
"restart-on": [
|
||||
"db-env"
|
||||
],
|
||||
"secrets-in-environment": "the mesh's own runtime reads MESH_STORE_* from the environment; a file twin in the SDK is the per-module work of issue 041"
|
||||
"database-url"
|
||||
]
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
// The module graph (novox/hq ADR 0070, ADR 0072).
|
||||
//
|
||||
// **This graph links module-versions to each other and knows nothing about nodes.** Which machine
|
||||
@@ -138,7 +139,9 @@ export class Graph {
|
||||
private constructor(private readonly pool: PgPool) {}
|
||||
|
||||
static fromEnv(env: NodeJS.ProcessEnv = process.env): Graph {
|
||||
const url = env["DATABASE_URL"];
|
||||
// As a file first (novox/hq ADR 0086): the connection string carries the password, and the
|
||||
// mesh writes it where only this process reads it; the plain variable remains for a hand-run.
|
||||
const url = env["DATABASE_URL"] ?? readMaybe(env["DATABASE_URL_FILE"]);
|
||||
if (!url) {
|
||||
throw new Error(
|
||||
"no DATABASE_URL: the catalogue holds the module graph and cannot hold it in memory, " +
|
||||
@@ -390,3 +393,13 @@ export class Graph {
|
||||
await this.pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
/** The content of a file the environment names, its line ending gone — or undefined when it names none. */
|
||||
function readMaybe(path: string | undefined): string | undefined {
|
||||
if (!path) return undefined;
|
||||
try {
|
||||
return readFileSync(path, "utf8").replace(/\r?\n$/, "");
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"type": "file",
|
||||
"path": "/var/lib/minio/root.env",
|
||||
"mode": "0600",
|
||||
"content": "MINIO_ROOT_USER=meshroot\nMINIO_ROOT_PASSWORD=${secret:root}\n"
|
||||
"content": "MINIO_ROOT_USER=meshroot\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
@@ -95,9 +95,12 @@
|
||||
"9000"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/minio/data/data1-1:/data"
|
||||
"/services/minio/data/data1-1:/data",
|
||||
"/var/lib/minio/root.secret:/run/secrets/root:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"env": {
|
||||
"MINIO_ROOT_PASSWORD_FILE": "/run/secrets/root"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "db-env",
|
||||
"id": "database-url",
|
||||
"type": "file",
|
||||
"path": "/var/lib/model-usage/db.env",
|
||||
"path": "/var/lib/model-usage/database.url",
|
||||
"mode": "0600",
|
||||
"content": "DATABASE_URL=postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\n"
|
||||
"content": "postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\n"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
@@ -53,15 +53,13 @@
|
||||
"network": "host",
|
||||
"volumes": [
|
||||
"/var/lib/mesh/model-usage/broker:/run/secrets/broker:ro",
|
||||
"/var/lib/model-usage:/run/state"
|
||||
"/var/lib/model-usage:/run/state",
|
||||
"/var/lib/model-usage/database.url:/run/secrets/database-url:ro"
|
||||
],
|
||||
"env": {
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/model-usage/db.env"
|
||||
],
|
||||
"secrets-in-environment": "the mesh's own runtime reads MESH_STORE_* from the environment; a file twin in the SDK is the per-module work of issue 041"
|
||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
||||
"DATABASE_URL_FILE": "/run/secrets/database-url"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
// The vendor-neutral usage store (novox/hq ADR 0054). ONE table holds BOTH grains — licence and
|
||||
// session — which differ only in `consumer`; a reading is one row `(licence, consumer, period,
|
||||
// metric, value)` plus its `raw` vendor payload. The store keeps the LATEST reading per
|
||||
@@ -47,7 +48,10 @@ export class UsageStore {
|
||||
/** Build a store from the resolved environment — DATABASE_URL is the granted postgres connection,
|
||||
* templated into the module's env-file from the mesh's binding (umami's DATABASE_URL precedent). */
|
||||
static fromEnv(env: NodeJS.ProcessEnv = process.env): UsageStore {
|
||||
return new UsageStore(new Pool({ connectionString: requireEnv("DATABASE_URL", env) }));
|
||||
// As a file first (novox/hq ADR 0086): the connection string carries the password.
|
||||
const url = env["DATABASE_URL"] ?? readMaybe(env["DATABASE_URL_FILE"]);
|
||||
if (!url) throw new Error("DATABASE_URL_FILE (or DATABASE_URL) is not set — model-usage cannot reach its database");
|
||||
return new UsageStore(new Pool({ connectionString: url }));
|
||||
}
|
||||
|
||||
/** Create the one table if it is not there. Run once by the migrate entry before the consumer
|
||||
@@ -84,3 +88,13 @@ export class UsageStore {
|
||||
await this.pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
/** The content of a file the environment names, its line ending gone — or undefined when it names none. */
|
||||
function readMaybe(path: string | undefined): string | undefined {
|
||||
if (!path) return undefined;
|
||||
try {
|
||||
return readFileSync(path, "utf8").replace(/\r?\n$/, "");
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"root": "/var/lib/mongodb/root.secret",
|
||||
"broker": "/var/lib/mesh/mongodb/broker"
|
||||
},
|
||||
"secrets-owner": "999:999",
|
||||
"resources": [
|
||||
{
|
||||
"id": "mesh-state",
|
||||
@@ -60,13 +61,6 @@
|
||||
"path": "/var/lib/mongodb/grants",
|
||||
"mode": "0700"
|
||||
},
|
||||
{
|
||||
"id": "root-env",
|
||||
"type": "file",
|
||||
"path": "/var/lib/mongodb/root.env",
|
||||
"mode": "0600",
|
||||
"content": "MONGO_INITDB_ROOT_PASSWORD=${secret:root}\n"
|
||||
},
|
||||
{
|
||||
"id": "data",
|
||||
"type": "directory",
|
||||
@@ -85,18 +79,16 @@
|
||||
"image": "mongo@sha256:e3fa459b4f4b72f3257c67a23c145e250b8b5700f033860392c68539b998bbe3",
|
||||
"network": "mongodb",
|
||||
"env": {
|
||||
"MONGO_INITDB_ROOT_USERNAME": "root"
|
||||
"MONGO_INITDB_ROOT_USERNAME": "root",
|
||||
"MONGO_INITDB_ROOT_PASSWORD_FILE": "/run/secrets/root"
|
||||
},
|
||||
"env-file": [
|
||||
"/var/lib/mongodb/root.env"
|
||||
],
|
||||
"ports": [
|
||||
"27017"
|
||||
],
|
||||
"volumes": [
|
||||
"/services/mongodb/db-data:/data/db"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"/services/mongodb/db-data:/data/db",
|
||||
"/var/lib/mongodb/root.secret:/run/secrets/root:ro"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
"volumes": [
|
||||
"/services/mssql/db-data:/var/opt/mssql"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the image documents only MSSQL_SA_PASSWORD, no _FILE and no configuration field; not convertible without a wrapper entrypoint"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
"volumes": [
|
||||
"/services/n8n/n8n-data:/home/node/.n8n"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "n8n's loader honours <VAR>_FILE for every setting; convertible, awaiting a bed that proves it (N8N_BASIC_AUTH_* was removed in n8n 1.0 and is likely dead)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"volumes": [
|
||||
"/services/nextcloud/html:/var/www/html"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the image honours POSTGRES_PASSWORD_FILE and NEXTCLOUD_ADMIN_PASSWORD_FILE (entrypoint file_env); OBJECTSTORE_S3_SECRET has none and needs a generated config fragment; convertible, awaiting a bed"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"/services/only-office/redis:/var/lib/redis",
|
||||
"/services/only-office/fonts:/usr/share/fonts/truetype/custom"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "run-document-server.sh regenerates JWT_SECRET from the environment on every start and overwrites local.json; not convertible"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"ports": [
|
||||
"9000"
|
||||
],
|
||||
"secrets-in-environment": "the server reads its settings from the environment; converting is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the application's own code reads MONGO_URL and MINIO_SECRET from the environment (photos server/src/config.js); converting is that repository's change"
|
||||
},
|
||||
{
|
||||
"id": "admin-client",
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"ports": [
|
||||
"8080"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "SEARXNG_SECRET is env-only, but settings.yml carries server.secret_key; convertible by mounting a generated settings.yml, not yet done"
|
||||
},
|
||||
{
|
||||
"id": "runtime-config",
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"/var/lib/step-ca:/home/step",
|
||||
"/var/lib/mesh/step-ca:/run/mesh:ro"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "the entrypoint honours DOCKER_STEPCA_INIT_PASSWORD_FILE; convertible, awaiting a bed that proves it"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"ports": [
|
||||
"3000"
|
||||
],
|
||||
"secrets-in-environment": "the image reads its configuration from the environment only; converting to a file is the per-module work of issue 041"
|
||||
"secrets-in-environment": "a Next.js/Prisma application: DATABASE_URL and APP_SECRET are read from the environment only; not convertible"
|
||||
},
|
||||
{
|
||||
"id": "runtime",
|
||||
|
||||
Reference in New Issue
Block a user