LoopbackTrustCheck¶
What it does¶
Auto-resolves the commit owner for requests originating from
trusted internal IP ranges — loopback (127.0.0.0/8, ::1) and
Tailscale CGNAT (100.64.0.0/10) by default. Lets internal services
co-located with signet (Rolling Memory, an internal MCP, your own
gateway) skip the X-Commit-Owner header dance for trusted traffic
without disabling owner enforcement for external callers.
Useful pattern: stack LoopbackTrustCheck before
OwnerResolutionCheck. Loopback traffic
auto-resolves to policy:internal-loopback; OwnerResolutionCheck
sees ctx.owner.is_resolved and passes through.
Stage¶
ADMISSION — runs before any forwarding decision.
Configuration¶
from signet.checks import LoopbackTrustCheck
# Defaults (loopback + Tailscale CGNAT)
LoopbackTrustCheck()
# Add additional trusted CIDRs (your own VPC range, Wireguard subnet, etc.)
LoopbackTrustCheck(extra_trusted_cidrs=("10.42.0.0/16", "fd7a:115c::/48"))
The default trusted set:
| CIDR | Purpose |
|---|---|
127.0.0.0/8 |
IPv4 loopback |
::1/128 |
IPv6 loopback |
100.64.0.0/10 |
RFC 6598 (Tailscale CGNAT range) |
Owner assignment¶
| Source IP | Resolved owner |
|---|---|
127.0.0.1 (loopback) |
policy:internal-loopback |
100.x.y.z (Tailscale CGNAT) |
policy:internal-tailnet:100.x.y.z |
Your extra_trusted_cidrs |
policy:internal-loopback (same as loopback) |
| Anything else | not resolved (next check runs) |
Audit row example¶
When loopback traffic resolves:
{
"owner_type": "policy",
"owner_id": "internal-loopback",
"approval_chain": ["policy:internal-loopback"],
"check_name": "loopback_trust",
"decision": "allow",
"reason": "loopback IP resolved to policy:internal-loopback"
}
Caveat¶
This check trusts the source IP. If your reverse proxy doesn't strip / overwrite a forwarded-for header that signet might mistake for the real source, an external caller could spoof their way into the trusted range. Make sure your reverse proxy sets the source IP signet sees to the actual peer, not whatever the caller claims.