Modei
PricingDocsBlog

Documentation

Anonymous Access Policy

Gates can offer degraded, read-only access to unauthenticated agents, serving as both enforcer and onboarding point. Invalid passports are always denied; they never fall back to anonymous access.

··

Anti-downgrade invariant, critical security rule

An agent that presents an expired, revoked, or otherwise invalid passport is always DENIED, it never falls back to anonymous access. This prevents attackers from intentionally using bad credentials to bypass constraints. Anonymous access only applies to agents that present no passport at all.

What is anonymous access?

Anonymous access allows a gate to serve unauthenticated agents, those that don't present a passport, with a limited, rate-limited, read-only subset of your API. This is useful for:

  • Service discovery and capability exploration before an agent gets a passport
  • Public read-only endpoints (search, catalog listing, documentation)
  • Onboarding flows where agents discover your service and then request credentials

Every anonymous response can include an upgrade_message and upgrade_url, so your gate doubles as an onboarding funnel for new agents.

Configuration

Configure anonymous access via the API or dashboard (Dashboard → Gates → [gate] → Anonymous Access).

bash
curl -X PUT \
  https://modei.ai/api/v1/gates/gate_my-api/anonymous-policy \
  -H "Authorization: Bearer mod_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "allowed_actions": ["api:search", "api:catalog"],
    "read_only": true,
    "rate_limit_per_minute": 5,
    "rate_limit_per_hour": 50,
    "upgrade_message": "Get a passport for full access, 10x rate limits, billing API, and service-level agreement (SLA) guarantees.",
    "upgrade_url": "https://myapi.com/get-access"
  }'

Policy fields

FieldTypeDescription
enabledbooleanToggle anonymous access on/off. Default: false.
allowed_actionsstring[]Which actions anonymous agents can request. Must be a subset of your catalog.
read_onlybooleanIf true, only read-only actions are permitted. Extra safety layer.
rate_limit_per_minutenumberMax requests per minute from any single anonymous agent.
rate_limit_per_hournumberMax requests per hour from any single anonymous agent.
upgrade_messagestringMessage included in every anonymous response explaining how to get full access.
upgrade_urlstringURL where the agent can obtain a passport.

MCP tools

set_anonymous_policy

Configure anonymous access for a gate.

json
{
  "gate_id": "gate_my-api",
  "enabled": true,
  "allowed_actions": ["api:search"],
  "rate_limit_per_minute": 5,
  "upgrade_url": "https://myapi.com/signup"
}

get_anonymous_policy

Read the current anonymous access configuration.

json
{
  "gate_id": "gate_my-api"
}

Example Flow: Graceful Onboarding

This is Use Case 7 from the feature inventory, an unknown agent discovering and upgrading to authenticated access.

1

Unknown agent queries without passport

Calls POST /api/gates/gate_my-api/check with no passport_id. Gate checks anonymous policy: action "api:search" is in allowed_actions, rate limit not exceeded → allow.

2

Response includes upgrade prompt

Every anonymous response includes: { "upgrade_message": "Get a passport for full access...", "upgrade_url": "https://myapi.com/signup" }

3

Agent attempts to use an expired passport

Presents a passport that expired last week. Gate sees a passport → runs full verification → passport is expired → decision: block. The anti-downgrade invariant prevents fallback to anonymous.

4

Agent's operator gets a passport

Operator signs up, creates an issuer, gets a passport with api:search and api:export permissions. Agent switches to authenticated access.

5

Agent now has full access

Authenticated calls have 10x rate limits, access to api:export, and full billing/metering. Anonymous access is no longer used.

Anti-Downgrade: The Security Invariant in Detail

The gate follows this strict logic for every request:

python
def check_gate(passport_id, action, target):
    if passport_id is None:
        # No passport presented
        if anonymous_policy.enabled and action in anonymous_policy.allowed_actions:
            return check_anonymous_rate_limits(action)  # allow or block
        return block("no_passport")

    # Passport was presented, run FULL verification
    passport = load_passport(passport_id)

    if passport is None:
        return block("passport_not_found")  # Never anonymous fallback

    if passport.revoked:
        return block("passport_revoked")    # Never anonymous fallback

    if passport.expires_at < now():
        return block("passport_expired")    # Never anonymous fallback

    # ... signature check, catalog pin check, permission check ...
    return allow if has_permission(passport, action) else block("no_permission")

The key invariant: any presented passport (even an invalid one) routes to the full verification path, never to the anonymous path. Only requests with no passport can use anonymous access.

Rate Limiting for Anonymous Agents

Anonymous rate limits are tracked per IP address or per agent identifier (if provided). The system maintains a rolling window counter in the anonymous_access_log table.

When an anonymous agent exceeds its rate limit, the gate returns:

json
{
  "decision": "block",
  "reason": "anonymous_rate_limit_exceeded",
  "upgrade_message": "Get a passport for full access, 10x rate limits",
  "upgrade_url": "https://myapi.com/get-access",
  "retry_after": 60
}

Related

  • Gates, Security profiles, verification logic, configuration.
  • Enforcement Layer, How constraints are evaluated for authenticated agents.
  • Gates API, Full anonymous-policy API reference.