mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 04:09:41 +02:00
Phase 1 of the Mission Key-Grant rollout. Webapp can now request a
wrapped per-mission data key; mana-ai can unwrap and (Phase 2) use it.
mana-auth:
- POST /api/v1/me/ai-mission-grant — HKDF-derives MDK from the user
master key, RSA-OAEP-2048-wraps with the mana-ai public key, returns
{ wrappedKey, derivation, issuedAt, expiresAt }
- MissionGrantService refuses zero-knowledge users (409 ZK_ACTIVE) and
returns 503 GRANT_NOT_CONFIGURED when MANA_AI_PUBLIC_KEY_PEM is unset
- TTL clamped to [1h, 30d]
mana-ai:
- configureMissionGrantKey + unwrapMissionGrant with structured failure
reasons (not-configured / expired / malformed / wrap-rejected)
- mana_ai.decrypt_audit table + RLS policy scoped to
app.current_user_id — append-only row per server-side decrypt attempt
- MANA_AI_PRIVATE_KEY_PEM env slot; absent = grants silently disabled
No existing behaviour changes: missions without a grant run exactly as
before. Grant flow is wired end-to-end but unused until Phase 2 lands
the encrypted resolver.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
export interface Config {
|
|
port: number;
|
|
databaseUrl: string;
|
|
syncDatabaseUrl: string;
|
|
baseUrl: string;
|
|
cookieDomain: string;
|
|
nodeEnv: string;
|
|
serviceKey: string;
|
|
cors: { origins: string[] };
|
|
manaNotifyUrl: string;
|
|
manaCreditsUrl: string;
|
|
manaSubscriptionsUrl: string;
|
|
manaMailUrl: string;
|
|
/** Base64-encoded 32-byte AES-256 key encryption key (KEK). Wraps each
|
|
* user's master key in auth.encryption_vaults. Required in production
|
|
* — in development a deterministic dev KEK is auto-generated so the
|
|
* service still boots, with a loud warning. */
|
|
encryptionKek: string;
|
|
/**
|
|
* PEM-encoded RSA-OAEP-2048 public key for the mana-ai Mission
|
|
* Grant runner. The `/me/ai-mission-grant` endpoint wraps per-
|
|
* mission data keys with this public key so only mana-ai (holder
|
|
* of the paired private key) can unwrap them. Optional at boot:
|
|
* when absent, the endpoint returns 503 so the UI can degrade
|
|
* to foreground-only execution.
|
|
*/
|
|
missionGrantPublicKeyPem?: string;
|
|
}
|
|
|
|
export function loadConfig(): Config {
|
|
const env = (key: string, fallback?: string) => process.env[key] || fallback || '';
|
|
const nodeEnv = env('NODE_ENV', 'development');
|
|
|
|
// Encryption KEK: in production a missing/short value is fatal — the
|
|
// vault service refuses to mint or unwrap any master keys without a
|
|
// real KEK. In development we auto-fill with a deterministic dev key
|
|
// so contributors can run the service without setting up a secret.
|
|
let encryptionKek = env('MANA_AUTH_KEK');
|
|
if (!encryptionKek) {
|
|
if (nodeEnv === 'production') {
|
|
throw new Error(
|
|
'mana-auth: MANA_AUTH_KEK env var is required in production. ' +
|
|
'Set it to a base64-encoded 32-byte random value: ' +
|
|
'`openssl rand -base64 32`'
|
|
);
|
|
}
|
|
// 32 zero bytes — deterministic, obviously not for production. The
|
|
// vault service logs a loud warning at startup when it sees this.
|
|
encryptionKek = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
|
|
}
|
|
|
|
return {
|
|
port: parseInt(env('PORT', '3001'), 10),
|
|
databaseUrl: env('DATABASE_URL', 'postgresql://mana:devpassword@localhost:5432/mana_platform'),
|
|
syncDatabaseUrl: env(
|
|
'SYNC_DATABASE_URL',
|
|
'postgresql://mana:devpassword@localhost:5432/mana_sync'
|
|
),
|
|
baseUrl: env('BASE_URL', 'http://localhost:3001'),
|
|
cookieDomain: env('COOKIE_DOMAIN'),
|
|
nodeEnv,
|
|
serviceKey: env('MANA_SERVICE_KEY', 'dev-service-key'),
|
|
cors: { origins: env('CORS_ORIGINS', 'http://localhost:5173').split(',') },
|
|
manaNotifyUrl: env('MANA_NOTIFY_URL', 'http://localhost:3013'),
|
|
manaCreditsUrl: env('MANA_CREDITS_URL', 'http://localhost:3061'),
|
|
manaSubscriptionsUrl: env('MANA_SUBSCRIPTIONS_URL', 'http://localhost:3063'),
|
|
manaMailUrl: env('MANA_MAIL_URL', 'http://localhost:3042'),
|
|
encryptionKek,
|
|
missionGrantPublicKeyPem: env('MANA_AI_PUBLIC_KEY_PEM') || undefined,
|
|
};
|
|
}
|