Add svc deployment plan, Gitea MCP config, update platform evolution
- docs/svc-deployment-plan.md: detailed plan for mc-proxy + MCNS on svc as the public edge (executed and live) - .mcp.json: Gitea MCP server config for Claude Code integration - PLATFORM_EVOLUTION.md: mark mc-proxy route persistence as done Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
.mcp.json
Normal file
12
.mcp.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"gitea": {
|
||||
"type": "stdio",
|
||||
"command": "/home/kyle/.local/bin/gitea-mcp",
|
||||
"env": {
|
||||
"GITEA_HOST": "https://git.wntrmute.dev",
|
||||
"GITEA_ACCESS_TOKEN": "873c9bf3ef872e11ca8811621dba0d7e3762cad9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,20 +230,21 @@ automated issuance flow.
|
||||
|
||||
**Depends on**: Metacrypt cert issuance policy (#7).
|
||||
|
||||
#### 5. mc-proxy: Route Persistence
|
||||
#### 5. mc-proxy: Route Persistence — DONE
|
||||
|
||||
**Gap**: mc-proxy loads routes from TOML on startup. Routes added via
|
||||
gRPC are lost on restart.
|
||||
|
||||
**Work**:
|
||||
- mc-proxy persists gRPC-managed routes in its SQLite database.
|
||||
- On startup, mc-proxy loads routes from the database.
|
||||
mc-proxy routes are fully persisted in SQLite and survive restarts:
|
||||
- SQLite `routes` table stores all listener and route state
|
||||
- TOML config seeds the database on first run only (via
|
||||
`store.IsEmpty()` + `store.Seed()`); subsequent starts load from
|
||||
DB (`store.ListListeners()` + `store.ListRoutes()`)
|
||||
- gRPC admin API (`AddRoute`/`RemoveRoute`) writes through to both
|
||||
DB and in-memory state
|
||||
- `mcproxyctl` CLI provides full route management (add, remove, list)
|
||||
- Routes added via gRPC survive mc-proxy restart
|
||||
- TOML route config is vestigial — kept only for mc-proxy's own
|
||||
bootstrap before MCP is operational. The gRPC API and mcproxyctl
|
||||
are the primary route management interfaces going forward.
|
||||
|
||||
**Depends on**: nothing (mc-proxy already has SQLite and gRPC API).
|
||||
|
||||
#### 6. MCP Agent: DNS Registration
|
||||
|
||||
**Gap**: DNS records are manually configured in MCNS zone files.
|
||||
@@ -310,7 +311,7 @@ The dependencies form a rough order:
|
||||
Phase A — Independent groundwork (parallel):
|
||||
#1 mcdsl proper module versioning ✓ DONE
|
||||
#2 MCP agent port assignment
|
||||
#5 mc-proxy route persistence
|
||||
#5 mc-proxy route persistence ✓ DONE
|
||||
#9 $PORT convention in applications
|
||||
|
||||
Phase B — MCP route registration:
|
||||
@@ -328,8 +329,9 @@ Phase D — DNS:
|
||||
(depends on #8)
|
||||
```
|
||||
|
||||
Phase A is partially complete. mcdsl is done. The remaining Phase A
|
||||
work (#2, #5, #9) can proceed in parallel.
|
||||
Phase A is partially complete. mcdsl (#1) and mc-proxy route
|
||||
persistence (#5) are done. The remaining Phase A work (#2, #9) can
|
||||
proceed in parallel.
|
||||
|
||||
After Phase A, services can be deployed with agent-assigned ports
|
||||
(manually registered in mc-proxy).
|
||||
@@ -346,15 +348,12 @@ Each phase is independently useful and deployable.
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
1. **mc-proxy route persistence (#5)** — mc-proxy already has SQLite
|
||||
and a gRPC API. Persist routes in the database, load on startup.
|
||||
This unblocks Phase B with no external dependencies.
|
||||
2. **MCP agent port assignment (#2)** — new service definition parser,
|
||||
port allocation, `$PORT_*` injection. Can develop in parallel with
|
||||
#5.
|
||||
3. **$PORT convention (#9)** — small per-service config change. Can
|
||||
1. **MCP agent port assignment (#2)** — new service definition parser,
|
||||
port allocation, `$PORT_*` injection. With #5 done, this is the
|
||||
remaining blocker for Phase B.
|
||||
2. **$PORT convention (#9)** — small per-service config change. Can
|
||||
start incrementally now, but only useful once #2 is deployed.
|
||||
4. **mcdoc implementation** — fully designed, no platform evolution
|
||||
3. **mcdoc implementation** — fully designed, no platform evolution
|
||||
dependency. Deployable with a manually assigned port today.
|
||||
|
||||
---
|
||||
|
||||
452
docs/svc-deployment-plan.md
Normal file
452
docs/svc-deployment-plan.md
Normal file
@@ -0,0 +1,452 @@
|
||||
# SVC Public Edge Deployment Plan
|
||||
|
||||
## Goal
|
||||
|
||||
Deploy mc-proxy and MCNS on `svc.metacircular.net` to make Metacircular
|
||||
platform services publicly accessible and delegate internal DNS zones.
|
||||
|
||||
## Current State
|
||||
|
||||
- **svc** (`svc.metacircular.net`): Debian 12, public IP, on tailnet as
|
||||
`svc`. Runs MCIAS only (systemd).
|
||||
- **rift** (`192.168.88.181` / `100.95.252.120`): NixOS, LAN + tailnet.
|
||||
Runs metacrypt, mc-proxy, mcr, mcns, mcp (all containers under mcp user).
|
||||
- **DNS**: `metacircular.net` managed at Hurricane Electric. Internal zones
|
||||
(`mcp.metacircular.net`, `svc.mcp.metacircular.net`) served by MCNS on
|
||||
rift, resolved via systemd-resolved split DNS on LAN clients.
|
||||
|
||||
## Architecture After Deployment
|
||||
|
||||
```
|
||||
Internet
|
||||
│
|
||||
▼
|
||||
svc.metacircular.net (public IP)
|
||||
├── MCIAS (:8443) ─── existing, no change
|
||||
├── mc-proxy (:443) ── NEW: L7 TLS termination
|
||||
│ └── metacrypt.metacircular.net → rift metacrypt-web (Tailscale)
|
||||
└── MCNS (:53) ─────── NEW: authoritative DNS for delegation
|
||||
└── mcp.metacircular.net, svc.mcp.metacircular.net zones
|
||||
|
||||
┌─── Tailscale ───┐
|
||||
▼ │
|
||||
rift (100.95.252.120) │
|
||||
├── mc-proxy (:443/:8443/:9443)
|
||||
├── metacrypt-web (:18080) ◄── forwarded from svc mc-proxy
|
||||
├── metacrypt-api (:18443)
|
||||
├── mcr, mcns, mcp, etc.
|
||||
```
|
||||
|
||||
## Phase 1: Build Binaries
|
||||
|
||||
Both mc-proxy and mcns need to be built for svc (x86_64 Linux, Debian).
|
||||
Since svc doesn't have Go, build on vade and scp.
|
||||
|
||||
```bash
|
||||
# On vade
|
||||
cd ~/src/metacircular/mc-proxy
|
||||
CGO_ENABLED=0 make mc-proxy
|
||||
scp mc-proxy svc:/tmp/
|
||||
|
||||
cd ~/src/metacircular/mcns
|
||||
CGO_ENABLED=0 make mcns
|
||||
scp mcns svc:/tmp/
|
||||
```
|
||||
|
||||
## Phase 2: TLS Certificates
|
||||
|
||||
Need a cert for `metacrypt.metacircular.net` (used by mc-proxy on svc for
|
||||
L7 termination). Also need a cert for MCNS management API on svc.
|
||||
|
||||
**Option A**: Issue from Metacrypt CA via the API.
|
||||
**Option B**: Generate self-signed from the wntrmute CA on rift.
|
||||
|
||||
Certs needed:
|
||||
- `metacrypt.metacircular.net` — for mc-proxy L7 route
|
||||
- `mcns-svc.svc.mcp.metacircular.net` — for MCNS management API (or self-signed)
|
||||
|
||||
The wntrmute CA cert (`wntrmute-ca.pem`) must be distributed to external
|
||||
users who want to trust the platform's TLS.
|
||||
|
||||
## Phase 3: Deploy mc-proxy on svc
|
||||
|
||||
### 3.1 Install
|
||||
|
||||
```bash
|
||||
# On svc (as root)
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin mc-proxy
|
||||
mkdir -p /srv/mc-proxy/{certs,backups}
|
||||
chown -R mc-proxy:mc-proxy /srv/mc-proxy
|
||||
chmod 0700 /srv/mc-proxy
|
||||
|
||||
install -m 0755 /tmp/mc-proxy /usr/local/bin/mc-proxy
|
||||
```
|
||||
|
||||
### 3.2 Configuration
|
||||
|
||||
Create `/srv/mc-proxy/mc-proxy.toml`:
|
||||
|
||||
```toml
|
||||
[database]
|
||||
path = "/srv/mc-proxy/mc-proxy.db"
|
||||
|
||||
# Public HTTPS — L7 termination for metacrypt web UI.
|
||||
# Backend is metacrypt-web on rift via Tailscale, re-encrypted with TLS.
|
||||
# mc-proxy skips backend cert verification (InsecureSkipVerify: trusted
|
||||
# internal backend), so SNI mismatch between the public hostname and
|
||||
# metacrypt-web's cert is not an issue.
|
||||
[[listeners]]
|
||||
addr = ":443"
|
||||
|
||||
[[listeners.routes]]
|
||||
hostname = "metacrypt.metacircular.net"
|
||||
backend = "100.95.252.120:18080"
|
||||
mode = "l7"
|
||||
tls_cert = "/srv/mc-proxy/certs/metacrypt.metacircular.net.pem"
|
||||
tls_key = "/srv/mc-proxy/certs/metacrypt.metacircular.net.key"
|
||||
backend_tls = true
|
||||
|
||||
# Firewall — internet-facing, aggressive blocking.
|
||||
[firewall]
|
||||
geoip_db = "/srv/mc-proxy/GeoLite2-Country.mmdb"
|
||||
blocked_ips = []
|
||||
blocked_cidrs = []
|
||||
blocked_countries = []
|
||||
rate_limit = 20
|
||||
rate_window = "10s"
|
||||
|
||||
# No gRPC admin API on svc (managed via config file only).
|
||||
|
||||
[proxy]
|
||||
connect_timeout = "5s"
|
||||
idle_timeout = "300s"
|
||||
shutdown_timeout = "30s"
|
||||
|
||||
[log]
|
||||
level = "info"
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `backend_tls = true` — metacrypt is security-sensitive; all hops must
|
||||
use TLS. mc-proxy's backend transport uses `InsecureSkipVerify: true`
|
||||
for trusted internal backends, so the public hostname / backend cert
|
||||
mismatch is not a problem.
|
||||
- **Rift prerequisite**: metacrypt-web must serve HTTPS and be exposed
|
||||
on rift's Tailscale interface. Currently mapped to `127.0.0.1:18080`.
|
||||
Two changes needed:
|
||||
1. Configure `web.tls_cert` and `web.tls_key` in metacrypt's config
|
||||
(metacrypt-web already supports TLS if these are set).
|
||||
2. Update the MCP service definition to expose the port on Tailscale:
|
||||
change `127.0.0.1:18080:8080` to `100.95.252.120:18080:8080`.
|
||||
3. Redeploy with `mcp deploy metacrypt`.
|
||||
|
||||
### 3.3 GeoIP Database
|
||||
|
||||
Download MaxMind GeoLite2-Country database (free, requires account):
|
||||
```bash
|
||||
# Download and install
|
||||
wget -O /srv/mc-proxy/GeoLite2-Country.mmdb <maxmind-url>
|
||||
chown mc-proxy:mc-proxy /srv/mc-proxy/GeoLite2-Country.mmdb
|
||||
```
|
||||
|
||||
Populate `blocked_countries` with appropriate list after initial
|
||||
deployment. Start empty, add based on access logs.
|
||||
|
||||
### 3.4 User Agent Blocking
|
||||
|
||||
UA blocking is L7-only and managed via the gRPC admin API or direct
|
||||
database insertion. Common bots to block:
|
||||
- Scanners: `zgrab`, `masscan`, `Nuclei`, `httpx`
|
||||
- AI scrapers: `GPTBot`, `CCBot`, `ClaudeBot`, `Bytespider`
|
||||
|
||||
Since we're not exposing the gRPC admin socket on svc, UA policies can
|
||||
be pre-seeded in the SQLite database or added later via gRPC.
|
||||
|
||||
### 3.5 Systemd Unit
|
||||
|
||||
Copy from mc-proxy deploy, adapting for svc:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=MC-Proxy TLS Router (svc)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=mc-proxy
|
||||
Group=mc-proxy
|
||||
ExecStart=/usr/local/bin/mc-proxy server --config /srv/mc-proxy/mc-proxy.toml
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictNamespaces=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
RestrictRealtime=true
|
||||
ReadWritePaths=/srv/mc-proxy
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 3.6 Enable
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now mc-proxy
|
||||
systemctl enable --now mc-proxy-backup.timer
|
||||
```
|
||||
|
||||
## Phase 4: Deploy MCNS on svc
|
||||
|
||||
### 4.1 Install
|
||||
|
||||
```bash
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin mcns
|
||||
mkdir -p /srv/mcns/{certs,backups}
|
||||
chown -R mcns:mcns /srv/mcns
|
||||
chmod 0700 /srv/mcns
|
||||
|
||||
install -m 0755 /tmp/mcns /usr/local/bin/mcns
|
||||
```
|
||||
|
||||
### 4.2 Configuration
|
||||
|
||||
Create `/srv/mcns/mcns.toml`:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
listen_addr = ":8444"
|
||||
grpc_addr = ":9444"
|
||||
tls_cert = "/srv/mcns/certs/mcns.pem"
|
||||
tls_key = "/srv/mcns/certs/mcns.key"
|
||||
|
||||
[database]
|
||||
path = "/srv/mcns/mcns.db"
|
||||
|
||||
[dns]
|
||||
listen_addr = ":53"
|
||||
upstreams = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||
|
||||
[mcias]
|
||||
server_url = "https://127.0.0.1:8443"
|
||||
ca_cert = ""
|
||||
service_name = "mcns"
|
||||
tags = []
|
||||
|
||||
[log]
|
||||
level = "info"
|
||||
```
|
||||
|
||||
Notes:
|
||||
- DNS on :53 (public-facing for NS delegation)
|
||||
- Management API on :8444/:9444 (different from rift's 8443/9443 to
|
||||
avoid confusion; not publicly exposed)
|
||||
- MCIAS at localhost since MCIAS runs on svc
|
||||
|
||||
### 4.3 Seed Data
|
||||
|
||||
MCNS creates the database and runs migrations on first start. Migration
|
||||
v2 seeds the same zones and records as rift's instance:
|
||||
- `svc.mcp.metacircular.net` zone (metacrypt, mcr, sgard, mcp-agent)
|
||||
- `mcp.metacircular.net` zone (rift, ns)
|
||||
|
||||
The `ns` record needs updating: for public delegation, `ns` should also
|
||||
have svc's public IP so external resolvers can reach it.
|
||||
|
||||
After first start, add svc's public IP to the ns record via the API:
|
||||
```bash
|
||||
# Get svc's public IP
|
||||
SVC_IP=$(curl -s ifconfig.me)
|
||||
|
||||
# Add ns record pointing to svc
|
||||
curl -k -X POST https://localhost:8444/v1/zones/mcp.metacircular.net/records \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\": \"ns\", \"type\": \"A\", \"value\": \"$SVC_IP\", \"ttl\": 300}"
|
||||
```
|
||||
|
||||
### 4.4 Systemd Unit
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=MCNS Authoritative DNS (svc)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=mcns
|
||||
Group=mcns
|
||||
ExecStart=/usr/local/bin/mcns server --config /srv/mcns/mcns.toml
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictNamespaces=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
RestrictRealtime=true
|
||||
ReadWritePaths=/srv/mcns
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 4.5 Enable
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now mcns
|
||||
systemctl enable --now mcns-backup.timer
|
||||
```
|
||||
|
||||
## Phase 5: DNS Changes at Hurricane Electric
|
||||
|
||||
### 5.1 A Record for metacrypt
|
||||
|
||||
```
|
||||
metacrypt.metacircular.net. A <svc-public-ip>
|
||||
```
|
||||
|
||||
### 5.2 NS Delegation for mcp.metacircular.net
|
||||
|
||||
```
|
||||
mcp.metacircular.net. NS ns.mcp.metacircular.net.
|
||||
ns.mcp.metacircular.net. A <svc-public-ip>
|
||||
```
|
||||
|
||||
This tells external resolvers: "for anything under mcp.metacircular.net,
|
||||
ask ns.mcp.metacircular.net (which is svc)." MCNS on svc then serves the
|
||||
authoritative answers.
|
||||
|
||||
**Note**: NS delegation requires the glue record (the A record for ns)
|
||||
to be in the parent zone at HE. Both records must be added.
|
||||
|
||||
### 5.3 Verification
|
||||
|
||||
```bash
|
||||
# From an external machine (not on tailnet)
|
||||
dig metacrypt.metacircular.net
|
||||
dig rift.mcp.metacircular.net
|
||||
dig metacrypt.svc.mcp.metacircular.net
|
||||
|
||||
# Test the web UI
|
||||
curl -k https://metacrypt.metacircular.net/
|
||||
```
|
||||
|
||||
## Phase 6: Verification
|
||||
|
||||
### 6.1 mc-proxy on svc
|
||||
|
||||
```bash
|
||||
# From vade
|
||||
curl -k https://metacrypt.metacircular.net/
|
||||
# Should show metacrypt web UI (login page)
|
||||
|
||||
# Check mc-proxy logs
|
||||
ssh svc journalctl -u mc-proxy -f
|
||||
```
|
||||
|
||||
### 6.2 MCNS on svc
|
||||
|
||||
```bash
|
||||
# Direct query to svc
|
||||
host rift.mcp.metacircular.net <svc-public-ip>
|
||||
host metacrypt.svc.mcp.metacircular.net <svc-public-ip>
|
||||
|
||||
# Forwarding works
|
||||
host google.com <svc-public-ip>
|
||||
|
||||
# Delegation (from external, after HE changes propagate)
|
||||
host rift.mcp.metacircular.net 8.8.8.8
|
||||
```
|
||||
|
||||
### 6.3 GeoIP and UA blocking
|
||||
|
||||
```bash
|
||||
# Check firewall is active
|
||||
ssh svc journalctl -u mc-proxy | grep -i "blocked\|firewall"
|
||||
|
||||
# Test UA blocking (after adding policies)
|
||||
curl -k -A "zgrab/0.x" https://metacrypt.metacircular.net/
|
||||
# Should get 403 Forbidden
|
||||
```
|
||||
|
||||
## Open Questions (Resolved)
|
||||
|
||||
1. **mc-proxy backend routing**: RESOLVED. mc-proxy uses
|
||||
`InsecureSkipVerify: true` for backend TLS — no SNI verification, no
|
||||
cert hostname checking. svc mc-proxy connects directly to
|
||||
metacrypt-web on rift's Tailscale IP with `backend_tls = true`. No
|
||||
SNI mismatch issue.
|
||||
|
||||
2. **GeoIP country list**: Start empty, add based on access logs after
|
||||
deployment. Review periodically.
|
||||
|
||||
3. **Record sync between MCNS instances**: Manual for now. Both instances
|
||||
get the same seed data from migration v2. Future: AXFR/IXFR zone
|
||||
transfers from rift (primary) to svc (secondary).
|
||||
|
||||
4. **MCIAS on svc for MCNS auth**: MCNS config uses
|
||||
`server_url = "https://127.0.0.1:8443"`. MCIAS cert is for
|
||||
`svc.metacircular.net`. Set `ca_cert` to the wntrmute CA cert and
|
||||
check if MCIAS cert includes localhost as a SAN. If not, either add
|
||||
localhost SAN or use `svc.metacircular.net` as the server_url.
|
||||
|
||||
5. **Backup coordination**: Stagger timers. mc-proxy at 02:00 UTC,
|
||||
mcns at 02:15 UTC, mcias at 02:30 UTC (check existing mcias timer).
|
||||
|
||||
## Rollback
|
||||
|
||||
If anything goes wrong:
|
||||
1. `systemctl stop mc-proxy mcns` on svc
|
||||
2. Remove DNS records at HE
|
||||
3. All traffic returns to existing paths (LAN via rift, MCIAS via svc)
|
||||
|
||||
No changes are made to rift's configuration. This deployment is purely
|
||||
additive on svc.
|
||||
|
||||
## Security Note: Backend TLS Verification
|
||||
|
||||
mc-proxy uses `InsecureSkipVerify: true` for backend TLS connections.
|
||||
This is acceptable because backend IPs are hardcoded operator-configured
|
||||
Tailscale addresses — WireGuard provides cryptographic peer
|
||||
authentication, so TLS cert hostname validation is redundant.
|
||||
|
||||
**Reconsider later**: when services have publicly-facing FQDNs (e.g.,
|
||||
`metacrypt.metacircular.net`), issue certs that include both the public
|
||||
FQDN and the internal name (`metacrypt.svc.mcp.metacircular.net`) as
|
||||
SANs. Then mc-proxy could enable backend cert verification for
|
||||
defense-in-depth. This is a low-priority improvement — Tailscale's
|
||||
identity guarantee is strong.
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **Let's Encrypt**: mc-proxy has ACME on its roadmap. Once implemented,
|
||||
public-facing routes could use trusted certs automatically.
|
||||
- **MCIAS migration to rift**: Once mc-proxy on svc handles public
|
||||
routing, MCIAS could move to rift. svc mc-proxy would forward auth
|
||||
traffic too. Eliminates the separate VPS dependency for MCIAS.
|
||||
- **Additional public services**: mcr web UI, MCP status dashboard, etc.
|
||||
Each would be an additional L7 route on svc's mc-proxy.
|
||||
Reference in New Issue
Block a user