{"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-analytics", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-auth", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-functions", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-imgproxy", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-kong", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-meta", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-realtime", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-rest", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-storage", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-studio", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "supabase-vector", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-analytics", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"privateAccessToken": "eW91ci1zdXBlci1zZWNyZXQtYW5kLWxvbmctbG9nZmxhcmUta2V5LXByaXZhdGU=", "publicAccessToken": "eW91ci1zdXBlci1zZWNyZXQtYW5kLWxvbmctbG9nZmxhcmUta2V5LXB1YmxpYw=="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-openai", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"apiKey": "a2V5X3N1cGVyX3NlY3JldA=="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-db", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"database": "cG9zdGdyZXM=", "password": "dnp4LndDNEFrZDR4LURqNldndWg=", "password_encoded": "dnp4LndDNEFrZDR4LURqNldndWg="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-jwt", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"anonKey": "ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnliMnhsSWpvaVlXNXZiaUlzSW1semN5STZJbXR1YjJVdGMzVndZV0poYzJVaUxDSnBZWFFpT2pFM056YzROekExTlRRc0ltVjRjQ0k2TWpBNU16SXpNRFUxTkgwLmdIUDdTcWE2TW1JOHBzbE1OZkt4WmhGVXpPS0FNWDUteGs3WFQwblFZcWc=", "secret": "NGU1M2UxZWNiOThlYjMyZWU4M2I4OTBiZWE3OWU2NzhlOTUyYzZhZTIzN2VkZjNmNjVmMzdiZjZkZDIyNjJlYg==", "serviceKey": "ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnliMnhsSWpvaWMyVnlkbWxqWlY5eWIyeGxJaXdpYVhOeklqb2lhMjV2WlMxemRYQmhZbUZ6WlNJc0ltbGhkQ0k2TVRjM056ZzNNRFUxTkN3aVpYaHdJam95TURrek1qTXdOVFUwZlEuQjBNMFY4VExaQzBoUzQ2OU8tbGVpY0R0OHVRRXFPY21EYWpsV1ppR1Zzdw=="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-meta", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"cryptoKey": "eW91ci1lbmNyeXB0aW9uLWtleS0zMi1jaGFycy1taW4="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-minio", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"password": "c2VjcmV0MTIzNA==", "user": "c3VwYS1zdG9yYWdl"}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-realtime", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"secretKeyBase": "VXBOVm50bjNjRHhISnBxOTlZTWMxVDFBUWdRcGM4a2ZZVHVSZ0JpWWExNUJMcng4ZXRRb1h6M2dadjEvdTJvcQ=="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-s3", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"accessKey": "NWZmNDBmMDY3NzI4YTc5ZGViMTNiMTc5Y2NkZmQxMTVkN2MwOTY2YmQxOGZhMzhkMjc5OGNmMzg5YTEzN2JmZA==", "keyId": "R0tmZTQ4NmE4NmVhMDg5MWY4ZTUzMzQ1NmE="}} --- {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "supabase-smtp", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "type": "Opaque", "data": {"password": "ZmFrZV9tYWlsX3Bhc3N3b3Jk", "username": "ZmFrZV9tYWlsX3VzZXI="}} --- {"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "supabase-functions-main", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "data": {"index.ts": "import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts'\n\nconsole.log('main function started')\n\nconst JWT_SECRET = Deno.env.get('JWT_SECRET')\nconst VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true'\n\nfunction getAuthToken(req: Request) {\nconst authHeader = req.headers.get('authorization')\nif (!authHeader) {\n throw new Error('Missing authorization header')\n}\nconst [bearer, token] = authHeader.split(' ')\nif (bearer !== 'Bearer') {\n throw new Error(`Auth header is not 'Bearer {token}'`)\n}\nreturn token\n}\n\nasync function verifyJWT(jwt: string): Promise {\nconst encoder = new TextEncoder()\nconst secretKey = encoder.encode(JWT_SECRET)\ntry {\n await jose.jwtVerify(jwt, secretKey)\n} catch (err) {\n console.error(err)\n return false\n}\nreturn true\n}\n\nDeno.serve(async (req: Request) => {\nif (req.method !== 'OPTIONS' && VERIFY_JWT) {\n try {\n const token = getAuthToken(req)\n const isValidJWT = await verifyJWT(token)\n\n if (!isValidJWT) {\n return new Response(JSON.stringify({ msg: 'Invalid JWT' }), {\n status: 401,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n } catch (e) {\n console.error(e)\n return new Response(JSON.stringify({ msg: e.toString() }), {\n status: 401,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n}\n\nconst url = new URL(req.url)\nconst { pathname } = url\nconst path_parts = pathname.split('/')\nconst service_name = path_parts[1]\n\nif (!service_name || service_name === '') {\n const error = { msg: 'missing function name in request' }\n return new Response(JSON.stringify(error), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nconst servicePath = `/home/deno/functions/${service_name}`\nconsole.error(`serving the request with ${servicePath}`)\n\nconst memoryLimitMb = 150\nconst workerTimeoutMs = 1 * 60 * 1000\nconst noModuleCache = false\nconst importMapPath = null\nconst envVarsObj = Deno.env.toObject()\nconst envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]])\n\ntry {\n const worker = await EdgeRuntime.userWorkers.create({\n servicePath,\n memoryLimitMb,\n workerTimeoutMs,\n noModuleCache,\n importMapPath,\n envVars,\n })\n return await worker.fetch(req)\n} catch (e) {\n const error = { msg: e.toString() }\n return new Response(JSON.stringify(error), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n})\n"}} --- {"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "supabase-kong", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "data": {"wrapper.sh": "#!/bin/bash\n\nset -euo pipefail\n\necho \"Replacing env placeholders of /usr/local/kong/kong.yml\"\n\nsed \\\n-e \"s|\\${SUPABASE_ANON_KEY}|${SUPABASE_ANON_KEY}|\" \\\n-e \"s|\\${SUPABASE_SERVICE_KEY}|${SUPABASE_SERVICE_KEY}|\" \\\n/usr/local/kong/template.yml \\\n> /usr/local/kong/kong.yml\n\nexec /docker-entrypoint.sh kong docker-start\n", "template.yml": "_format_version: '2.1'\n_transform: true\n\nconsumers:\n - username: anon\n keyauth_credentials:\n - key: ${SUPABASE_ANON_KEY}\n - username: service_role\n keyauth_credentials:\n - key: ${SUPABASE_SERVICE_KEY}\nacls:\n - consumer: anon\n group: anon\n - consumer: service_role\n group: admin\nservices:\n # Dedicated health endpoint used by the GCE LB BackendConfig.\n # request-termination returns 200 synchronously without hitting any\n # upstream, so the probe passes as long as the Kong proxy itself is\n # accepting requests -- same liveness semantics as the TCP check we\n # originally wanted, but using the HTTP protocol that GCE's L7\n # BackendConfig CRD actually accepts (TCP is rejected with\n # `Protocol \"TCP\" is not valid, must be one of [HTTP,HTTPS,HTTP2]`).\n #\n # URL is a RFC-2606 reserved `.invalid` hostname that never resolves.\n # We originally tried `http://127.0.0.1:8000/` here, which is Kong's\n # own proxy port -- this crashlooped the pod on startup (rollout timed\n # out waiting for new pod to become Ready). Suspected cause: Kong's\n # declarative-config parser rejects the self-reference. Since the\n # request-termination plugin short-circuits before any DNS lookup or\n # upstream connection, using a non-resolvable placeholder URL is\n # equivalent in behavior but avoids the loop detection.\n - name: healthz\n url: http://knoe.healthz.invalid/\n routes:\n - name: healthz\n strip_path: true\n paths:\n - /healthz\n plugins:\n - name: request-termination\n config:\n status_code: 200\n message: ok\n # Stop-gap support endpoint -- 302s to mailto:support@knoe.dev so any user\n # who lands at https://db.0.knoe.dev/support gets a working escape hatch.\n # The in-Studio \"Report a problem\" / \"Send feedback\" buttons in upstream\n # Studio are hardcoded to supabase.com endpoints and are not yet rewired\n # (would require forking the Studio image -- tracked in docs/TODO.md).\n # In the meantime we tell users: \"for help, go to db.0.knoe.dev/support\".\n # request-termination synthesizes a 302 status; response-transformer\n # injects the Location header (request-termination alone can't set it).\n - name: support\n url: http://knoe.support.invalid/\n routes:\n - name: support-redirect\n strip_path: true\n paths:\n - /support\n plugins:\n - name: request-termination\n config:\n status_code: 302\n message: \"Redirecting to support@knoe.dev\"\n - name: response-transformer\n config:\n add:\n headers:\n - \"Location:mailto:support@knoe.dev\"\n - name: auth-v1-open\n url: http://supabase-auth:9999/verify\n routes:\n - name: auth-v1-open\n strip_path: true\n paths:\n - /auth/v1/verify\n plugins:\n - name: cors\n - name: auth-v1-open-callback\n url: http://supabase-auth:9999/callback\n routes:\n - name: auth-v1-open-callback\n strip_path: true\n paths:\n - /auth/v1/callback\n plugins:\n - name: cors\n - name: auth-v1-open-authorize\n url: http://supabase-auth:9999/authorize\n routes:\n - name: auth-v1-open-authorize\n strip_path: true\n paths:\n - /auth/v1/authorize\n plugins:\n - name: cors\n - name: auth-v1\n _comment: \"GoTrue: /auth/v1/* -> http://supabase-auth:9999/*\"\n url: http://supabase-auth:9999\n routes:\n - name: auth-v1-all\n strip_path: true\n paths:\n - /auth/v1/\n plugins:\n - name: cors\n - name: key-auth\n config:\n hide_credentials: false\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n - anon\n - name: rest-v1\n _comment: \"PostgREST: /rest/v1/* -> http://supabase-rest:3000/*\"\n url: http://supabase-rest:3000/\n routes:\n - name: rest-v1-all\n strip_path: true\n paths:\n - /rest/v1/\n plugins:\n - name: cors\n - name: key-auth\n config:\n hide_credentials: true\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n - anon\n - name: graphql-v1\n _comment: 'PostgREST: /graphql/v1/* -> http://supabase-rest:3000/rpc/graphql'\n url: http://supabase-rest:3000/rpc/graphql\n routes:\n - name: graphql-v1-all\n strip_path: true\n paths:\n - /graphql/v1\n plugins:\n - name: cors\n - name: key-auth\n config:\n hide_credentials: true\n - name: request-transformer\n config:\n add:\n headers:\n - Content-Profile:graphql_public\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n - anon\n - name: realtime-v1-ws\n _comment: \"Realtime: /realtime/v1/* -> ws://supabase-realtime:4000/socket/*\"\n url: http://supabase-realtime:4000/socket\n protocol: ws\n routes:\n - name: realtime-v1-ws\n strip_path: true\n paths:\n - /realtime/v1/\n plugins:\n - name: cors\n - name: key-auth\n config:\n hide_credentials: false\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n - anon\n - name: realtime-v1-rest\n _comment: 'Realtime: /realtime/v1/* -> http://supabase-realtime:4000/api/*'\n url: http://supabase-realtime:4000/api\n protocol: http\n routes:\n - name: realtime-v1-rest\n strip_path: true\n paths:\n - /realtime/v1/api\n plugins:\n - name: cors\n - name: key-auth\n config:\n hide_credentials: false\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n - anon\n - name: storage-v1\n _comment: \"Storage: /storage/v1/* -> http://supabase-storage:5000/*\"\n url: http://supabase-storage:5000/\n routes:\n - name: storage-v1-all\n strip_path: true\n paths:\n - /storage/v1/\n plugins:\n - name: cors\n - name: functions-v1\n _comment: 'Edge Functions: /functions/v1/* -> http://supabase-functions:9000/*'\n url: http://supabase-functions:9000/\n routes:\n - name: functions-v1-all\n strip_path: true\n paths:\n - /functions/v1/\n plugins:\n - name: cors\n\n\n\n\n - name: meta\n _comment: \"pg-meta: /pg/* -> http://supabase-meta:8080/*\"\n url: http://supabase-meta:8080/\n routes:\n - name: meta-all\n strip_path: true\n paths:\n - /pg/\n plugins:\n - name: key-auth\n config:\n hide_credentials: false\n - name: acl\n config:\n hide_groups_header: true\n allow:\n - admin\n # Onboarding reveal page (knoe-onboard nginx pod). Listed BEFORE the\n # /-wildcard dashboard route so Kong's longest-prefix match picks\n # /onboard.html over the dashboard. The page itself is also exempted\n # from oauth2-proxy gating (`--skip-auth-route=^/onboard\\.html$`) since\n # URL fragments don't survive an OAuth redirect; URL secrecy + 24h\n # expiry + immediate-rotation is the security envelope.\n - name: onboard\n _comment: 'Onboarding reveal: /onboard.html -> knoe-onboard nginx pod'\n url: http://knoe-onboard:80/\n routes:\n - name: onboard-html\n strip_path: false\n paths:\n - /onboard.html\n plugins:\n - name: cors\n - name: dashboard\n _comment: 'Studio: /* -> http://supabase-studio:3000/*'\n url: http://supabase-studio:3000/\n routes:\n - name: dashboard-all\n strip_path: true\n paths:\n - /\n plugins:\n - name: cors\n"}} --- {"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "supabase-vector-config", "labels": {"helm.sh/chart": "knoe-supabase-0.5.0-knoe.1", "app.kubernetes.io/name": "supabase", "app.kubernetes.io/instance": "supabase", "app.kubernetes.io/managed-by": "Helm"}}, "data": {"vector.yml": "api:\n enabled: true\n address: 0.0.0.0:9001\n\nsources:\n kubernetes_host:\n type: kubernetes_logs\n extra_label_selector: app.kubernetes.io/instance=supabase,app.kubernetes.io/name!=knoe-supabase-vector\n\ntransforms:\n project_logs:\n type: remap\n inputs:\n - kubernetes_host\n source: |-\n .project = \"default\"\n .event_message = del(.message)\n .appname = del(.kubernetes.container_name)\n del(.file)\n del(.kubernetes)\n del(.source_type)\n del(.stream)\n router:\n type: route\n inputs:\n - project_logs\n route:\n kong: '.appname == \"knoe-supabase-kong\"'\n auth: '.appname == \"knoe-supabase-auth\"'\n rest: '.appname == \"knoe-supabase-rest\"'\n realtime: '.appname == \"knoe-supabase-realtime\"'\n storage: '.appname == \"knoe-supabase-storage\"'\n functions: '.appname == \"knoe-supabase-functions\"'\n db: '.appname == \"knoe-supabase-db\"'\n # Ignores non nginx errors since they are related with kong booting up\n kong_logs:\n type: remap\n inputs:\n - router.kong\n source: |-\n req, err = parse_nginx_log(.event_message, \"combined\")\n if err == null {\n .timestamp = req.timestamp\n .metadata.request.headers.referer = req.referer\n .metadata.request.headers.user_agent = req.agent\n .metadata.request.headers.cf_connecting_ip = req.client\n .metadata.response.status_code = req.status\n url, split_err = split(req.request, \" \")\n if split_err == null {\n .metadata.request.method = url[0]\n .metadata.request.path = url[1]\n .metadata.request.protocol = url[2]\n }\n }\n if err != null {\n abort\n }\n # Ignores non nginx errors since they are related with kong booting up\n kong_err:\n type: remap\n inputs:\n - router.kong\n source: |-\n .metadata.request.method = \"GET\"\n .metadata.response.status_code = 200\n parsed, err = parse_nginx_log(.event_message, \"error\")\n if err == null {\n .timestamp = parsed.timestamp\n .severity = parsed.severity\n .metadata.request.host = parsed.host\n .metadata.request.headers.cf_connecting_ip = parsed.client\n url, err = split(parsed.request, \" \")\n if err == null {\n .metadata.request.method = url[0]\n .metadata.request.path = url[1]\n .metadata.request.protocol = url[2]\n }\n }\n if err != null {\n abort\n }\n # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency.\n auth_logs:\n type: remap\n inputs:\n - router.auth\n source: |-\n parsed, err = parse_json(.event_message)\n if err == null {\n .metadata.timestamp = parsed.time\n .metadata = merge!(.metadata, parsed)\n }\n # PostgREST logs are structured so we separate timestamp from message using regex\n rest_logs:\n type: remap\n inputs:\n - router.rest\n source: |-\n parsed, err = parse_regex(.event_message, r'^(?P