Skip to content

Add tier-based API-key rate limits for continuous Shutter API users #131

Description

@ylembachar

Description

Currently, all authenticated Shutter API keys use the same @withApiKey rate limits. This prevents us from granting higher limits to continuous-use customers without raising limits for every authenticated user.

Introduce mutually exclusive API-key tiers with centrally managed rate-limit policies.

Proposed implementation

  • Add an optional tier column to keys.csv.
  • Treat existing two-column rows as standard for backward compatibility.
  • Define endpoint limits and tier multipliers in apikeys/limits.yaml.
  • Support the following tiers:
    • unauthorized: anonymous access, counted per source IP
    • standard: existing authenticated limits
    • premium: increased limits for continuous users
  • Generate separate Caddy matchers for each tier.
  • Generate all rate-limit zones from limits.yaml.
  • Preserve /check_authentication for valid keys from every tier.
  • Reject unknown tiers and malformed policy configuration during compilation.
  • Print the resolved limits and number of keys per tier during compilation.
  • Document key creation, tier changes, recompilation, and Caddy restart procedures.

Target limits

Endpoint Anonymous Standard Premium
/time/register_identity 5/day 500/day 2,500/day
/time/get_data_for_encryption 10/day 1,000/day 5,000/day
/time/get_decryption_key 20/day 2,000/day 10,000/day
/decrypt_commitment 10/day 1,000/day 5,000/day
/event/compile_trigger_definition 20/day 2,000/day 10,000/day
/event/register_identity 5/day 500/day 2,500/day
/event/get_data_for_encryption 10/day 1,000/day 5,000/day
/event/get_trigger_expiration_block 20/day 2,000/day 10,000/day
/event/get_decryption_key 20/day 2,000/day 10,000/day

Acceptance criteria

  • Existing two-column keys.csv files continue working.
  • Keys without an explicit tier receive standard limits.
  • Standard and premium keys match only their respective rate-limit blocks.
  • Invalid or missing keys remain subject to anonymous, IP-based limits.
  • A tier containing no keys cannot accidentally match all requests.
  • /check_authentication recognizes valid keys from every assignable tier.
  • Unknown tier names cause compilation to fail with a useful error.
  • Malformed limits.yaml configuration fails before Caddy starts.
  • Generated Caddy configuration passes caddy validate.
  • Public documentation distinguishes standard and premium limits.
  • Standard, premium, invalid-key, and anonymous behavior is verified on a staging droplet.
  • A premium key completes a short continuous time-based decryption run without encountering standard-tier limits.

Deployment validation

  1. Create separate standard and premium test keys.
  2. Compile the API-key and rate-limit configuration.
  3. Review the printed effective-limit table.
  4. Validate the generated Caddy configuration.
  5. Restart Caddy and confirm it loads the generated configuration.
  6. Test /check_authentication with no key, an invalid key, a standard key, and a premium key.
  7. Confirm each request is handled by the expected tier.
  8. Test the 429 boundary using a temporary short-window policy.
  9. Restore the intended production policy.
  10. Run a short continuous time-based decryption test using the premium key.

Operational considerations

  • Rate-limit counters are held in Caddy memory and reset when Caddy restarts.
  • Changing a key's tier requires recompiling the snippet and restarting Caddy.
  • Test keys must not belong to customers.
  • API keys must not be printed in logs or committed to the repository.
  • Registration limits are also spending limits because each registration consumes gas from the shared API signer.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions