{ "module": "letta", "version": "1", "capabilities": [ "container-runtime" ], "requires": [ "postgres-database", "route" ], "contributes": { "postgres-database": { "name": "letta", "extensions": [ "vector" ] }, "route": { "label": "letta", "endpoint": "web" } }, "binds": { "postgres-database": "${dir:state}/database.json", "route": "${dir:state}/route.json" }, "secrets": { "postgres-database": "${dir:state}/database.secret" }, "own-secrets": { "server-password": { "path": "${dir:state}/server-password.secret", "taken": "at-start" }, "openai-api-key": { "path": "${dir:state}/openai-api-key.secret", "taken": "at-start", "issued-by": "outside" } }, "listens": [ { "name": "web", "port": 8283, "protocol": "tcp", "from": "mesh", "why": "the Letta agent server REST API and web UI, password-protected (--secure); a public name is the route's" } ], "resources": [ { "id": "mesh-state", "type": "directory", "mode": "0700", "place": "mesh" }, { "id": "state", "type": "directory", "mode": "0700", "place": "." }, { "id": "server-env", "type": "file", "path": "${dir:state}/server.env", "mode": "0600", "content": "LETTA_PG_URI=postgresql://${bound:postgres-database:as}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}\nPGPASSFILE=/run/secrets/pgpass\nLETTA_SERVER_PASSWORD=${secret:server-password}\nOPENAI_API_KEY=${secret:openai-api-key}\nSECURE=true\nTZ=Europe/Brussels\n" }, { "id": "pgpass", "type": "file", "path": "${dir:state}/pgpass", "mode": "0600", "content": "*:*:*:${bound:postgres-database:as}:${secret:postgres-database}\n" }, { "id": "start", "type": "file", "path": "${dir:state}/start.sh", "mode": "0644", "content": "#!/bin/sh\n# Generated by the mesh. Do not edit: module letta writes this file and replaces it at every push.\n#\n# letta 0.6.8 prints secrets it is given to its log (novox/hq issue 268):\n# letta/server/rest_api/app.py prints its server password when it starts in secure mode;\n# startup.sh, alembic/env.py and letta/server/server.py print LETTA_PG_URI whole.\n# The URI carries no password (libpq reads it from PGPASSFILE), and the one print of the server\n# password is rewritten before the server starts. Either one failing refuses the start: a letta\n# that does not start says why here, and one that leaks says nothing.\nset -e\napp=/app/letta/server/rest_api/app.py\nsed -i 's/Using secure mode with password: {random_password}/Using secure mode (the password is not printed)/' \"$app\"\nif grep -q 'print(.*random_password' \"$app\"; then\n echo \"letta: $app still prints the server password; not starting (novox/hq issue 268)\" >&2\n exit 1\nfi\ncase \"$LETTA_PG_URI\" in\n *://*:*@*)\n echo \"letta: LETTA_PG_URI carries a password, and letta prints that URI; not starting (novox/hq issue 268)\" >&2\n exit 1\n ;;\nesac\nexec ./letta/server/startup.sh\n" }, { "id": "net", "type": "network", "name": "letta" }, { "id": "server", "type": "container", "name": "letta", "image": "letta/letta@sha256:bfd1e49ce45b9a208c941e832c1d1d194017ff210a3784b0ca6c323aed767a29", "network": "letta", "env-file": [ "${dir:state}/server.env" ], "ports": [ "8283" ], "volumes": [ "${dir:state}/pgpass:/run/secrets/pgpass:ro", "${dir:state}/start.sh:/run/letta/start.sh:ro" ], "args": [ "sh", "/run/letta/start.sh" ], "secrets-in-environment": "letta 0.6.x reads its settings from the environment only (pydantic settings, no secrets_dir or _FILE twin): LETTA_SERVER_PASSWORD and OPENAI_API_KEY have no file source. The database password is not here: LETTA_PG_URI, which letta prints at start, names no password, and libpq reads it from the mounted pgpass file (PGPASSFILE)" }, { "id": "runtime-config", "type": "file", "path": "${dir:mesh-state}/config.json", "mode": "0600", "content": "{\n \"password\": \"${secret:server-password}\"\n}\n", "merge": "json" } ], "build": { "artifacts": [ { "name": "tools", "kind": "bundle", "language": "typescript", "entrypoints": [ "tools/index.js" ], "loads": [ "tools/index.js" ], "env": { "MESH_LETTA_URL": "http://127.0.0.1:${port:8283}", "MESH_LETTA_CONFIG_FILE": "${dir:mesh-state}/config.json" } } ] } }