mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 06:09:08 +03:00
chore: initial import of envoy test config
This commit is contained in:
1 parent
21d7ac0518
commit
3525fe6159
6 files changed
+622
-1
No files matched your search
@@ -0,0 +1,42 @@
|
||||
# Envoy override for Kong
|
||||
# Usage: docker compose -f docker-compose.yml -f docker-compose.envoy.yml up
|
||||
|
||||
services:
|
||||
# Disable the original Kong service
|
||||
kong:
|
||||
profiles:
|
||||
- disabled
|
||||
|
||||
# Envoy API gateway
|
||||
api:
|
||||
container_name: supabase-envoy
|
||||
image: envoyproxy/envoy:v1.28.0
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- ${KONG_HTTP_PORT}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT:-8443}:8443/tcp
|
||||
- 9901:9901/tcp
|
||||
volumes:
|
||||
- ./volumes/api/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro
|
||||
- ./volumes/api/envoy/cds.yaml:/etc/envoy/cds.yaml:ro
|
||||
- ./volumes/api/envoy/lds.yaml.template:/etc/envoy/lds.yaml.template:ro
|
||||
- ./volumes/api/envoy/docker-entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
ANON_KEY: ${ANON_KEY}
|
||||
SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
|
||||
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
|
||||
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
|
||||
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:9901/ready"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- kong
|
||||
- envoy
|
||||
@@ -0,0 +1,160 @@
|
||||
resources:
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: auth
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: auth
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: auth
|
||||
port_value: 9999
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: rest
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: rest
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: rest
|
||||
port_value: 3000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: realtime
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: realtime
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: realtime-dev.supabase-realtime
|
||||
port_value: 4000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: storage
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: storage
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: storage
|
||||
port_value: 5000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: functions
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: functions
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: functions
|
||||
port_value: 9000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: analytics
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: analytics
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: analytics
|
||||
port_value: 4000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: meta
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: meta
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: meta
|
||||
port_value: 8080
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
|
||||
- '@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
|
||||
name: studio
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: studio
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: studio
|
||||
port_value: 3000
|
||||
circuit_breakers:
|
||||
thresholds:
|
||||
- priority: DEFAULT
|
||||
max_connections: 10000
|
||||
max_pending_requests: 10000
|
||||
max_requests: 10000
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Generate base64 encoded basic auth credentials for dashboard
|
||||
# Format: username:password -> base64
|
||||
DASHBOARD_BASIC_AUTH=$(printf '%s' "${DASHBOARD_USERNAME}:${DASHBOARD_PASSWORD}" | base64 | tr -d '\n')
|
||||
|
||||
echo "Generating Envoy configuration..."
|
||||
|
||||
# Process the lds.yaml template with environment variables using sed
|
||||
# Using | as delimiter since JWT tokens contain /
|
||||
sed -e "s|\${ANON_KEY}|${ANON_KEY}|g" \
|
||||
-e "s|\${SERVICE_ROLE_KEY}|${SERVICE_ROLE_KEY}|g" \
|
||||
-e "s|\${DASHBOARD_BASIC_AUTH}|${DASHBOARD_BASIC_AUTH}|g" \
|
||||
/etc/envoy/lds.yaml.template > /etc/envoy/lds.yaml
|
||||
|
||||
echo "Envoy configuration generated successfully"
|
||||
echo "Starting Envoy..."
|
||||
|
||||
# Start Envoy
|
||||
exec envoy -c /etc/envoy/envoy.yaml "$@"
|
||||
@@ -0,0 +1,24 @@
|
||||
dynamic_resources:
|
||||
cds_config:
|
||||
path_config_source:
|
||||
path: /etc/envoy/cds.yaml
|
||||
resource_api_version: V3
|
||||
lds_config:
|
||||
path_config_source:
|
||||
path: /etc/envoy/lds.yaml
|
||||
resource_api_version: V3
|
||||
node:
|
||||
cluster: supabase_cluster
|
||||
id: supabase_node
|
||||
overload_manager:
|
||||
resource_monitors:
|
||||
- name: envoy.resource_monitors.global_downstream_max_connections
|
||||
typed_config:
|
||||
'@type': >-
|
||||
type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig
|
||||
max_active_downstream_connections: 30000
|
||||
admin:
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 9901
|
||||
@@ -0,0 +1,374 @@
|
||||
resources:
|
||||
- '@type': type.googleapis.com/envoy.config.listener.v3.Listener
|
||||
name: http_listener
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 8000
|
||||
filter_chains:
|
||||
- filters:
|
||||
- name: envoy.filters.network.http_connection_manager
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||
stat_prefix: ingress_http
|
||||
upgrade_configs:
|
||||
- upgrade_type: websocket
|
||||
access_log:
|
||||
- name: envoy.access_loggers.stdout
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
|
||||
log_format:
|
||||
text_format_source:
|
||||
inline_string: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - - [%START_TIME(%d/%b/%Y:%H:%M:%S %z)%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %BYTES_SENT% \"%REQ(REFERER)%\" \"%REQ(USER-AGENT)%\"\n"
|
||||
http_filters:
|
||||
# CORS filter
|
||||
- name: envoy.filters.http.cors
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
|
||||
|
||||
# RBAC filter for API key validation
|
||||
- name: envoy.filters.http.rbac
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
|
||||
rules:
|
||||
action: DENY
|
||||
policies:
|
||||
# Deny requests without valid API key to protected routes
|
||||
api_key_required:
|
||||
permissions:
|
||||
- and_rules:
|
||||
rules:
|
||||
# Match protected paths that need API key
|
||||
- or_rules:
|
||||
rules:
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /auth/v1/
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /rest/v1/
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /graphql/v1
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /realtime/v1/
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /pg/
|
||||
# Exclude open auth routes
|
||||
- not_rule:
|
||||
or_rules:
|
||||
rules:
|
||||
- url_path:
|
||||
path:
|
||||
exact: /auth/v1/verify
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /auth/v1/verify?
|
||||
- url_path:
|
||||
path:
|
||||
exact: /auth/v1/callback
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /auth/v1/callback?
|
||||
- url_path:
|
||||
path:
|
||||
exact: /auth/v1/authorize
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /auth/v1/authorize?
|
||||
principals:
|
||||
# Deny if no valid API key
|
||||
- not_id:
|
||||
or_ids:
|
||||
ids:
|
||||
- header:
|
||||
name: apikey
|
||||
string_match:
|
||||
exact: "${ANON_KEY}"
|
||||
- header:
|
||||
name: apikey
|
||||
string_match:
|
||||
exact: "${SERVICE_ROLE_KEY}"
|
||||
- header:
|
||||
name: Authorization
|
||||
string_match:
|
||||
prefix: "Bearer ${ANON_KEY}"
|
||||
- header:
|
||||
name: Authorization
|
||||
string_match:
|
||||
prefix: "Bearer ${SERVICE_ROLE_KEY}"
|
||||
# /pg/ requires service_role only
|
||||
pg_service_role_only:
|
||||
permissions:
|
||||
- url_path:
|
||||
path:
|
||||
prefix: /pg/
|
||||
principals:
|
||||
- not_id:
|
||||
or_ids:
|
||||
ids:
|
||||
- header:
|
||||
name: apikey
|
||||
string_match:
|
||||
exact: "${SERVICE_ROLE_KEY}"
|
||||
- header:
|
||||
name: Authorization
|
||||
string_match:
|
||||
prefix: "Bearer ${SERVICE_ROLE_KEY}"
|
||||
|
||||
# Lua filter for removing apikey from forwarded requests where hide_credentials is needed
|
||||
- name: envoy.filters.http.lua
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
|
||||
default_source_code:
|
||||
inline_string: |
|
||||
function envoy_on_request(request_handle)
|
||||
end
|
||||
source_codes:
|
||||
hide_apikey:
|
||||
inline_string: |
|
||||
function envoy_on_request(request_handle)
|
||||
request_handle:headers():remove("apikey")
|
||||
-- Also strip apikey from query string
|
||||
local path = request_handle:headers():get(":path")
|
||||
if path then
|
||||
path = path:gsub("&apikey=[^&]*", ""):gsub("%?apikey=[^&]*&", "?"):gsub("%?apikey=[^&]*$", "")
|
||||
request_handle:headers():replace(":path", path)
|
||||
end
|
||||
end
|
||||
add_graphql_header:
|
||||
inline_string: |
|
||||
function envoy_on_request(request_handle)
|
||||
request_handle:headers():add("Content-Profile", "graphql_public")
|
||||
request_handle:headers():remove("apikey")
|
||||
local path = request_handle:headers():get(":path")
|
||||
if path then
|
||||
path = path:gsub("&apikey=[^&]*", ""):gsub("%?apikey=[^&]*&", "?"):gsub("%?apikey=[^&]*$", "")
|
||||
request_handle:headers():replace(":path", path)
|
||||
end
|
||||
end
|
||||
|
||||
# Router filter
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
route_config:
|
||||
name: local_route
|
||||
virtual_hosts:
|
||||
- name: supabase_services
|
||||
domains:
|
||||
- "*"
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.cors:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy
|
||||
allow_origin_string_match:
|
||||
- safe_regex:
|
||||
regex: ".*"
|
||||
allow_methods: GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,TRACE,CONNECT
|
||||
allow_headers: "*"
|
||||
expose_headers: "*"
|
||||
max_age: "3600"
|
||||
allow_credentials: true
|
||||
routes:
|
||||
# Block /api/mcp
|
||||
- match:
|
||||
prefix: /api/mcp
|
||||
direct_response:
|
||||
status: 403
|
||||
body:
|
||||
inline_string: "Access is forbidden."
|
||||
|
||||
# Block /mcp
|
||||
- match:
|
||||
prefix: /mcp
|
||||
direct_response:
|
||||
status: 403
|
||||
body:
|
||||
inline_string: "Access is forbidden."
|
||||
|
||||
# Open Auth routes (no API key required)
|
||||
- match:
|
||||
path: /auth/v1/verify
|
||||
route:
|
||||
cluster: auth
|
||||
prefix_rewrite: /verify
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
- match:
|
||||
prefix: /auth/v1/verify
|
||||
route:
|
||||
cluster: auth
|
||||
regex_rewrite:
|
||||
pattern:
|
||||
regex: ^/auth/v1/verify
|
||||
substitution: /verify
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
- match:
|
||||
path: /auth/v1/callback
|
||||
route:
|
||||
cluster: auth
|
||||
prefix_rewrite: /callback
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
- match:
|
||||
prefix: /auth/v1/callback
|
||||
route:
|
||||
cluster: auth
|
||||
regex_rewrite:
|
||||
pattern:
|
||||
regex: ^/auth/v1/callback
|
||||
substitution: /callback
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
- match:
|
||||
path: /auth/v1/authorize
|
||||
route:
|
||||
cluster: auth
|
||||
prefix_rewrite: /authorize
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
- match:
|
||||
prefix: /auth/v1/authorize
|
||||
route:
|
||||
cluster: auth
|
||||
regex_rewrite:
|
||||
pattern:
|
||||
regex: ^/auth/v1/authorize
|
||||
substitution: /authorize
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
# Secure Auth routes (API key required, credentials NOT hidden)
|
||||
- match:
|
||||
prefix: /auth/v1/
|
||||
route:
|
||||
cluster: auth
|
||||
prefix_rewrite: /
|
||||
timeout: 30s
|
||||
|
||||
# REST routes (API key required, credentials hidden)
|
||||
- match:
|
||||
prefix: /rest/v1/
|
||||
route:
|
||||
cluster: rest
|
||||
prefix_rewrite: /
|
||||
timeout: 120s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.lua:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
|
||||
name: hide_apikey
|
||||
|
||||
# GraphQL routes (API key required, add Content-Profile header)
|
||||
- match:
|
||||
prefix: /graphql/v1
|
||||
route:
|
||||
cluster: rest
|
||||
prefix_rewrite: /rpc/graphql
|
||||
timeout: 120s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.lua:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
|
||||
name: add_graphql_header
|
||||
|
||||
# Realtime WebSocket routes
|
||||
- match:
|
||||
prefix: /realtime/v1/
|
||||
route:
|
||||
cluster: realtime
|
||||
prefix_rewrite: /socket/
|
||||
timeout: 0s
|
||||
upgrade_configs:
|
||||
- upgrade_type: websocket
|
||||
|
||||
# Realtime REST API
|
||||
- match:
|
||||
prefix: /realtime/v1/api
|
||||
route:
|
||||
cluster: realtime
|
||||
prefix_rewrite: /api
|
||||
timeout: 30s
|
||||
|
||||
# Storage routes (no API key required - storage handles its own auth)
|
||||
- match:
|
||||
prefix: /storage/v1/
|
||||
route:
|
||||
cluster: storage
|
||||
prefix_rewrite: /
|
||||
timeout: 300s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
# Edge Functions routes (no API key required - functions handle their own auth)
|
||||
- match:
|
||||
prefix: /functions/v1/
|
||||
route:
|
||||
cluster: functions
|
||||
prefix_rewrite: /
|
||||
timeout: 300s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
# Analytics routes (no API key required)
|
||||
- match:
|
||||
prefix: /analytics/v1/
|
||||
route:
|
||||
cluster: analytics
|
||||
prefix_rewrite: /
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
|
||||
# pg-meta routes (service_role only)
|
||||
- match:
|
||||
prefix: /pg/
|
||||
route:
|
||||
cluster: meta
|
||||
prefix_rewrite: /
|
||||
timeout: 30s
|
||||
|
||||
# Studio/Dashboard (catch-all with basic auth via RBAC)
|
||||
- match:
|
||||
prefix: /
|
||||
route:
|
||||
cluster: studio
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.rbac:
|
||||
'@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
|
||||
rbac:
|
||||
rules:
|
||||
action: DENY
|
||||
policies:
|
||||
require_basic_auth:
|
||||
permissions:
|
||||
- any: true
|
||||
principals:
|
||||
- not_id:
|
||||
header:
|
||||
name: authorization
|
||||
string_match:
|
||||
exact: "Basic ${DASHBOARD_BASIC_AUTH}"
|
||||
@@ -30,7 +30,7 @@ transforms:
|
||||
inputs:
|
||||
- project_logs
|
||||
route:
|
||||
kong: '.appname == "supabase-kong"'
|
||||
kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"'
|
||||
auth: '.appname == "supabase-auth"'
|
||||
rest: '.appname == "supabase-rest"'
|
||||
realtime: '.appname == "realtime-dev.supabase-realtime"'
|
||||
|
||||
Reference in new issue
Block a user