Files
overwatch/simulation/scripts/bootstrap_auth.py
thebadking 388d2fb1d0
Some checks failed
AERIS-10 CI / Python Lint + Tests (push) Has been cancelled
AERIS-10 CI / MCU Firmware Tests (push) Has been cancelled
AERIS-10 CI / FPGA Regression (push) Has been cancelled
AERIS-10 CI / Cross-Layer Contract Tests (push) Has been cancelled
Record the Overwatch tree as a new root.
The operator scenario uses ten radars on a 12 km ring, a 9 km loiter, a blast that waits for the closest point, and up to three launches when a closer drone is on the line.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 07:36:33 +00:00

83 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Generate stable local auth secrets. Nothing privileged is emitted to stdout."""
import base64
import hashlib
import hmac
import json
import os
from pathlib import Path
import secrets
import time
ROOT = Path(__file__).resolve().parents[1]
AUTH_DIR = ROOT / 'deploy/supabase'
def read_env(path):
return dict(line.split('=', 1) for line in path.read_text().splitlines() if line and not line.startswith('#'))
def jwt(secret, role):
def encode(value):
return base64.urlsafe_b64encode(json.dumps(value, separators=(',', ':')).encode()).rstrip(b'=').decode()
now = int(time.time())
message = encode({'alg': 'HS256', 'typ': 'JWT'}) + '.' + encode({'role': role, 'iss': 'supabase', 'iat': now, 'exp': now + 10 * 365 * 86400})
signature = base64.urlsafe_b64encode(hmac.new(secret.encode(), message.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
return message + '.' + signature
def write_private(path, content):
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, 'w') as output:
output.write(content)
path.chmod(0o600)
def main():
env_path = AUTH_DIR / '.env'
if not env_path.exists():
secret = secrets.token_hex(32)
demo_path = ROOT / 'DEMO_USER.txt'
demo = dict(line.split(': ', 1) for line in demo_path.read_text().splitlines() if ': ' in line) if demo_path.exists() else {}
values = {
'POSTGRES_PASSWORD': secrets.token_hex(24),
'JWT_SECRET': secret,
'SUPABASE_ANON_KEY': jwt(secret, 'anon'),
'SUPABASE_SERVICE_KEY': jwt(secret, 'service_role'),
'SUPABASE_PORT': os.getenv('SUPABASE_PORT', '54321'),
'KONG_BIND_ADDRESS': os.getenv('KONG_BIND_ADDRESS', '0.0.0.0'),
'SITE_URL': os.getenv('SITE_URL', 'https://defense.naori.cloud'),
'DEMO_EMAIL': demo.get('Username', 'demo@defense.naori.cloud'),
'DEMO_PASSWORD': demo.get('Password', secrets.token_urlsafe(18)),
}
write_private(env_path, ''.join(f'{key}={value}\n' for key, value in values.items()))
print('Generated auth secrets and demo credentials in deploy/supabase/.env')
values = read_env(env_path)
config = {
'_format_version': '2.1', '_transform': True,
'consumers': [
{'username': 'anon', 'keyauth_credentials': [{'key': values['SUPABASE_ANON_KEY']}]},
{'username': 'service_role', 'keyauth_credentials': [{'key': values['SUPABASE_SERVICE_KEY']}]},
],
'services': [
{'name': name, 'url': url, 'routes': [{'name': name, 'paths': [path], 'strip_path': True}],
'plugins': [{'name': 'cors'}, {'name': 'key-auth', 'config': {'hide_credentials': False}}]}
for name, url, path in [('auth', 'http://auth:9999/', '/auth/v1/'), ('rest', 'http://rest:3000/', '/rest/v1/')]
],
}
config['services'].extend([
{'name': 'simulation', 'url': 'http://host.docker.internal:8000',
'routes': [{'name': 'simulation', 'paths': ['/socket.io/'], 'strip_path': False}],
'read_timeout': 60000, 'write_timeout': 60000},
{'name': 'frontend', 'url': 'http://frontend:80',
'routes': [{'name': 'frontend', 'paths': ['/'], 'strip_path': False}]},
])
# Kong runs as UID 1337; its bind-mounted config must be readable inside the container.
write_private(AUTH_DIR / 'kong.json.local', json.dumps(config, indent=2))
(AUTH_DIR / 'kong.json.local').chmod(0o644)
write_private(ROOT / 'deploy/auth.env.local', f"SUPABASE_URL=http://127.0.0.1:{values['SUPABASE_PORT']}\nSUPABASE_ANON_KEY={values['SUPABASE_ANON_KEY']}\n")
if __name__ == '__main__':
main()