# nshekhawat/portcullis architecture

> Architecture diagram of the GitHub repository nshekhawat/portcullis, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-27). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/nshekhawat/portcullis
- Repository: https://github.com/nshekhawat/portcullis

## Overview

Portcullis is a rate-limiting service with a fast enforcement plane and a separate, asynchronous judgment plane that assigns policy tiers to suspected L7 abuse. Requests enter through its HTTP/gRPC API or reverse-proxy gateway, consume tokens against configured rules and state, and receive an allow/deny result. In parallel, observations feed periodic aggregation and detection; bounded judge calls inform guardrails, which control tier changes and audit decisions. The README documents admin controls, metrics, and multiple judge backends; unsampled implementation details are represented only where the tree supports a path.

Main entry points: [`internal/controller/controller.go`](https://github.com/nshekhawat/portcullis/blob/main/internal/controller/controller.go) (Cycle controller), [`internal/ratelimiter/limiter.go`](https://github.com/nshekhawat/portcullis/blob/main/internal/ratelimiter/limiter.go) (Rate limiter), [`internal/gateway/gateway.go`](https://github.com/nshekhawat/portcullis/blob/main/internal/gateway/gateway.go) (Proxy gateway), [`internal/judge/judge.go`](https://github.com/nshekhawat/portcullis/blob/main/internal/judge/judge.go) (Judge interface)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_entry["Request entry"]
  node_gateway["Proxy gateway<br/>[gateway.go]"]
  node_http["HTTP API<br/>[http.go]"]
  node_grpc["gRPC API<br/>[grpc.go]"]
end

subgraph group_enforce["Rate enforcement"]
  node_limiter["Rate limiter<br/>[limiter.go]"]
  node_bucket["Token bucket<br/>[tokenbucket.go]"]
  node_rules["Rate rules<br/>[config.go]"]
  node_storage[("Bucket storage<br/>[storage.go]")]
  node_memory[("Memory backend<br/>[memory.go]")]
  node_redis[("Redis backend<br/>[redis.go]")]
end

subgraph group_judgment["Judgment plane"]
  node_aggregator["Signal aggregation<br/>[aggregator.go]"]
  node_detector["Suspect detection<br/>[detector.go]"]
  node_controller["Cycle controller<br/>[controller.go]"]
  node_judge["Judge interface<br/>[judge.go]"]
  node_rulesjudge["Rules judge<br/>[rules.go]"]
  node_typesafe["TypeSafe judge<br/>[client.go]"]
  node_openai["OpenAI-compatible judge<br/>[client.go]"]
  node_guardrails["Tier guardrails<br/>[policy.go]"]
  node_tiers[("Tier policy store<br/>[policy.go]")]
end

subgraph group_operations["Operations"]
  node_admin["Admin API<br/>[admin.go]"]
  node_audit[("Decision audit<br/>[audit.go]")]
  node_metrics["Metrics<br/>[metrics.go]"]
end

node_caller(("API caller"))
node_upstream["Protected upstream"]
node_model(("Judge endpoint"))

node_caller -->|"checks via"| node_http
node_caller -->|"checks via"| node_grpc
node_caller -->|"sends traffic"| node_gateway
node_http -->|"checks limits"| node_limiter
node_grpc -->|"checks limits"| node_limiter
node_gateway -->|"checks limits"| node_limiter
node_gateway -->|"proxies allowed"| node_upstream
node_limiter -->|"uses rules"| node_rules
node_limiter -->|"consumes tokens"| node_bucket
node_bucket -->|"reads/writes state"| node_storage
node_storage -->|"implemented by"| node_memory
node_storage -->|"implemented by"| node_redis
node_gateway -->|"observes traffic"| node_aggregator
node_aggregator -->|"provides signals"| node_controller
node_controller -->|"selects suspects"| node_detector
node_controller -->|"requests verdicts"| node_judge
node_judge -.->|"can dispatch"| node_rulesjudge
node_judge -.->|"can dispatch"| node_typesafe
node_judge -.->|"can dispatch"| node_openai
node_typesafe -.->|"posts judgments"| node_model
node_openai -.->|"posts judgments"| node_model
node_controller -->|"constrains verdicts"| node_guardrails
node_controller -->|"applies tier changes"| node_tiers
node_controller -->|"records decisions"| node_audit
node_admin -->|"manages tiers"| node_tiers
node_admin -->|"reads decisions"| node_audit
node_http -->|"records metrics"| node_metrics
node_grpc -->|"records metrics"| node_metrics
node_controller -->|"reports cycles"| node_metrics

click node_gateway "https://github.com/nshekhawat/portcullis/blob/main/internal/gateway/gateway.go"
click node_http "https://github.com/nshekhawat/portcullis/blob/main/internal/server/http.go"
click node_grpc "https://github.com/nshekhawat/portcullis/blob/main/internal/server/grpc.go"
click node_limiter "https://github.com/nshekhawat/portcullis/blob/main/internal/ratelimiter/limiter.go"
click node_bucket "https://github.com/nshekhawat/portcullis/blob/main/internal/ratelimiter/tokenbucket.go"
click node_rules "https://github.com/nshekhawat/portcullis/blob/main/internal/config/config.go"
click node_storage "https://github.com/nshekhawat/portcullis/blob/main/internal/storage/storage.go"
click node_memory "https://github.com/nshekhawat/portcullis/blob/main/internal/storage/memory.go"
click node_redis "https://github.com/nshekhawat/portcullis/blob/main/internal/storage/redis.go"
click node_aggregator "https://github.com/nshekhawat/portcullis/blob/main/internal/signals/aggregator.go"
click node_detector "https://github.com/nshekhawat/portcullis/blob/main/internal/detect/detector.go"
click node_controller "https://github.com/nshekhawat/portcullis/blob/main/internal/controller/controller.go"
click node_judge "https://github.com/nshekhawat/portcullis/blob/main/internal/judge/judge.go"
click node_rulesjudge "https://github.com/nshekhawat/portcullis/blob/main/internal/judge/rules/rules.go"
click node_typesafe "https://github.com/nshekhawat/portcullis/blob/main/internal/judge/typesafe/client.go"
click node_openai "https://github.com/nshekhawat/portcullis/blob/main/internal/judge/openai/client.go"
click node_guardrails "https://github.com/nshekhawat/portcullis/blob/main/internal/policy/policy.go"
click node_tiers "https://github.com/nshekhawat/portcullis/blob/main/internal/policy/policy.go"
click node_admin "https://github.com/nshekhawat/portcullis/blob/main/internal/server/admin.go"
click node_audit "https://github.com/nshekhawat/portcullis/blob/main/internal/controller/audit.go"
click node_metrics "https://github.com/nshekhawat/portcullis/blob/main/internal/metrics/metrics.go"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_gateway,node_http,node_grpc toneBlue
class node_limiter,node_bucket,node_rules,node_storage,node_memory,node_redis toneAmber
class node_aggregator,node_detector,node_controller,node_judge,node_rulesjudge,node_typesafe,node_openai,node_guardrails,node_tiers,node_caller toneMint
class node_admin,node_audit,node_metrics toneRose
class node_upstream,node_model toneIndigo
```

## Components

### Request entry

- [Proxy gateway](https://github.com/nshekhawat/portcullis/blob/main/internal/gateway/gateway.go): `internal/gateway/gateway.go`
- [HTTP API](https://github.com/nshekhawat/portcullis/blob/main/internal/server/http.go): `internal/server/http.go`
- [gRPC API](https://github.com/nshekhawat/portcullis/blob/main/internal/server/grpc.go): `internal/server/grpc.go`

### Rate enforcement

- [Rate limiter](https://github.com/nshekhawat/portcullis/blob/main/internal/ratelimiter/limiter.go): `internal/ratelimiter/limiter.go`
- [Token bucket](https://github.com/nshekhawat/portcullis/blob/main/internal/ratelimiter/tokenbucket.go): `internal/ratelimiter/tokenbucket.go`
- [Rate rules](https://github.com/nshekhawat/portcullis/blob/main/internal/config/config.go): `internal/config/config.go`
- [Bucket storage](https://github.com/nshekhawat/portcullis/blob/main/internal/storage/storage.go): `internal/storage/storage.go`
- [Memory backend](https://github.com/nshekhawat/portcullis/blob/main/internal/storage/memory.go): `internal/storage/memory.go`
- [Redis backend](https://github.com/nshekhawat/portcullis/blob/main/internal/storage/redis.go): `internal/storage/redis.go`

### Judgment plane

- [Signal aggregation](https://github.com/nshekhawat/portcullis/blob/main/internal/signals/aggregator.go): `internal/signals/aggregator.go`
- [Suspect detection](https://github.com/nshekhawat/portcullis/blob/main/internal/detect/detector.go): `internal/detect/detector.go`
- [Cycle controller](https://github.com/nshekhawat/portcullis/blob/main/internal/controller/controller.go): `internal/controller/controller.go`
- [Judge interface](https://github.com/nshekhawat/portcullis/blob/main/internal/judge/judge.go): `internal/judge/judge.go`
- [Rules judge](https://github.com/nshekhawat/portcullis/blob/main/internal/judge/rules/rules.go): `internal/judge/rules/rules.go`
- [TypeSafe judge](https://github.com/nshekhawat/portcullis/blob/main/internal/judge/typesafe/client.go): `internal/judge/typesafe/client.go`
- [OpenAI-compatible judge](https://github.com/nshekhawat/portcullis/blob/main/internal/judge/openai/client.go): `internal/judge/openai/client.go`
- [Tier guardrails](https://github.com/nshekhawat/portcullis/blob/main/internal/policy/policy.go): `internal/policy/policy.go`
- [Tier policy store](https://github.com/nshekhawat/portcullis/blob/main/internal/policy/policy.go): `internal/policy/policy.go`

### Operations

- [Admin API](https://github.com/nshekhawat/portcullis/blob/main/internal/server/admin.go): `internal/server/admin.go`
- [Decision audit](https://github.com/nshekhawat/portcullis/blob/main/internal/controller/audit.go): `internal/controller/audit.go`
- [Metrics](https://github.com/nshekhawat/portcullis/blob/main/internal/metrics/metrics.go): `internal/metrics/metrics.go`

### Other components

- **API caller**
- **Protected upstream**
- **Judge endpoint**

## Connections

- API caller → HTTP API: checks via
- API caller → gRPC API: checks via
- API caller → Proxy gateway: sends traffic
- HTTP API → Rate limiter: checks limits
- gRPC API → Rate limiter: checks limits
- Proxy gateway → Rate limiter: checks limits
- Proxy gateway → Protected upstream: proxies allowed
- Rate limiter → Rate rules: uses rules
- Rate limiter → Token bucket: consumes tokens
- Token bucket → Bucket storage: reads/writes state
- Bucket storage → Memory backend: implemented by
- Bucket storage → Redis backend: implemented by
- Proxy gateway → Signal aggregation: observes traffic
- Signal aggregation → Cycle controller: provides signals
- Cycle controller → Suspect detection: selects suspects
- Cycle controller → Judge interface: requests verdicts
- Judge interface → Rules judge: can dispatch
- Judge interface → TypeSafe judge: can dispatch
- Judge interface → OpenAI-compatible judge: can dispatch
- TypeSafe judge → Judge endpoint: posts judgments
- OpenAI-compatible judge → Judge endpoint: posts judgments
- Cycle controller → Tier guardrails: constrains verdicts
- Cycle controller → Tier policy store: applies tier changes
- Cycle controller → Decision audit: records decisions
- Admin API → Tier policy store: manages tiers
- Admin API → Decision audit: reads decisions
- HTTP API → Metrics: records metrics
- gRPC API → Metrics: records metrics
- Cycle controller → Metrics: reports cycles
