This is a handover-oriented requirements specification for version 1 of Nordray White Rabbit.
The intended .NET root namespace is:
Nordray.WhiteRabbit
This document is written for a less experienced developer who should be able to start implementation from it.
Nordray White Rabbit is an open source OIDC facade in front of selected bunny.net APIs.
Its purpose is to make bunny.net APIs available through modern user login and delegated client access, even when bunny.net itself does not provide native OIDC login for these APIs.
White Rabbit will:
- require OIDC authentication for all proxied requests
- map each supported bunny.net endpoint to an internal capability
- allow a signed-in user to grant a client access to a selected set of capabilities
- proxy allowed requests to bunny.net from the server side
- keep bunny credentials on the server side only
- deny everything that is not explicitly supported
Version 1 is intentionally simple:
- capability-based access only
- no resource-level scoping
- no time-based constraints
- no OpenFGA
- no edge scripting
- no generic reverse proxy behaviour
Product name:
- Nordray White Rabbit
.NET namespace root:
Nordray.WhiteRabbit
Suggested repository layout:
/src
Nordray.WhiteRabbit.Web
Nordray.WhiteRabbit.Core
Nordray.WhiteRabbit.Infrastructure
Nordray.WhiteRabbit.Database
Nordray.WhiteRabbit.Proxy
Nordray.WhiteRabbit.Bunny
Nordray.WhiteRabbit.AuthProxy
/tests
Nordray.WhiteRabbit.Tests.Unit
Nordray.WhiteRabbit.Tests.Integration
/deploy
/docs
The system must:
- allow a human user to sign in with email-based one-time code authentication
- expose OIDC to clients through Dex
- show a consent screen where a signed-in user approves requested capabilities
- store grants in SQLite
- accept requests only for supported bunny endpoints
- check whether the authenticated client has the required capability
- forward allowed requests to bunny.net
- inject bunny credentials server side
- log audit events for security and debugging
The system should:
- be easy to run locally with Docker Compose
- be easy to deploy to bunny.net Magic Containers as one application with multiple containers
- use SQLite for White Rabbit data
- be understandable without requiring Kubernetes or cloud-native tooling
- be conservative and secure by default
- ASP.NET Core application
- YARP-based proxy forwarding
- Dex as separate OIDC service
- custom email code login in front of Dex
- capability-based grants
- SQLite persistence
- consent screen
- admin/configuration through files or a minimal admin UI
- audit logging
- Docker Compose for local development
- container images suitable for bunny.net Magic Containers
- OpenFGA
- resource-level permissions
- attribute-based conditions
- refresh-token-heavy advanced flows
- Bunny Edge Scripting
- dynamic endpoint discovery at runtime
- support for every bunny.net API endpoint on day one
- multi-tenant isolation inside White Rabbit
- passwords
- social login
- SAML
- fine-grained secrets manager integration
White Rabbit proxies selected bunny.net APIs.
Relevant facts:
- bunny.net publishes OpenAPI specification files for several API families
- the documented API families include Core Platform API, Origin Errors API, Storage API, Stream API, Shield API and Edge Scripting API
- bunny.net documents
AccessKeyheader authentication for account-specific actions - Stream and Storage use the same header name but different credentials
- Magic Containers supports multiple containers in the same application, running in one pod sandbox and communicating over
localhost - Magic Containers requires images from a private container registry
These facts are useful for design and deployment.
Dex is used as the OIDC provider exposed to White Rabbit clients.
Important constraints:
- Dex
authproxyis suitable for login methods not natively handled by Dex - Dex expects the authenticating proxy to set a user identity header such as
X-Remote-User - the proxy must strip any client-supplied
X-Remote-*headers before forwarding to Dex - the Dex authproxy connector does not support refresh tokens
This is acceptable for v1.
+---------------------------+
| browser / cli / app |
+-------------+-------------+
|
| OIDC
v
+---------------------------+
| Dex |
| separate container |
+-------------+-------------+
^
| authenticated callback via authproxy
|
+-------------+-------------+
| Nordray.WhiteRabbit |
| ASP.NET Core |
| |
| - email code login |
| - consent UI |
| - grants |
| - capability checks |
| - YARP forwarding |
| - audit |
| - Bunny credential use |
+------+------+-------------+
| |
| +-------------------------> bunny.net APIs
|
+-------------------------------> SQLite
Dex handles identity protocols.
White Rabbit handles product logic:
- one-time code login
- grant storage
- capability checks
- proxy decisions
- bunny credential injection
- audit logging
Everything must run locally using Docker Compose.
Required services:
white-rabbitdex
Optional local helper services:
mailpitor equivalent for local email testing- a small reverse proxy only if needed for local HTTPS, but not required for v1
Local persistent files:
./data/white-rabbit/white-rabbit.db./data/dex/dex.dbor Dex storage equivalent- optional local mail catcher state
The application must be deployable as one bunny.net Magic Containers application with multiple containers.
Target container set:
white-rabbitdex
Containers should communicate via localhost on different ports.
Suggested port usage:
- White Rabbit:
8080 - Dex:
5556
No port collisions are allowed.
Use a private container registry for images.
Persistent storage requirement:
- White Rabbit SQLite file must be placed on persistent storage if long-lived data is required across restarts and redeployments
- if Dex uses persistent local storage, it must also be placed on persistent storage or otherwise treated as replaceable depending on chosen configuration
Version 1 must use:
- email address
- one-time numeric or alphanumeric code
- short expiration time
- no password storage
Example flow:
- user enters email address
- White Rabbit generates one-time code
- White Rabbit sends code by email
- user enters the code
- White Rabbit verifies the code
- White Rabbit forwards the authenticated user to Dex authproxy callback
- Dex issues OIDC tokens to the client
This exists because Dex provides OIDC, but the chosen user login method is implemented in White Rabbit itself.
The system must:
- store only hashed codes if practical
- expire codes after a short duration, for example 10 minutes
- enforce a resend cooldown
- enforce rate limits per email and per IP
- invalidate a code after successful use
- support local development with a fake SMTP target
- support production SMTP configuration through environment variables
White Rabbit must act as the authenticating proxy in front of Dex callback handling.
Requirements:
- White Rabbit must strip any incoming
X-Remote-*headers from untrusted client requests - White Rabbit must set the configured identity header itself only after successful email-code authentication
- White Rabbit must forward the request to Dex callback exactly as needed for the authproxy connector flow
- the user identity should be based on verified email address
Dex is the OIDC provider seen by external clients.
Version 1 assumptions:
- White Rabbit clients are registered OIDC clients in Dex
- White Rabbit itself trusts tokens issued by Dex
- White Rabbit reads the authenticated user identity and client identity from Dex-issued claims
Open point for implementation:
- decide whether all clients are confidential clients in v1, or whether public clients are also allowed
Recommended v1 choice:
- begin with confidential clients only if possible, because it is simpler and safer
Version 1 uses a simple capability-based authorisation model.
There is no:
- resource-level grant
- per-zone grant
- conditional policy engine
- ownership model
Every supported proxied bunny operation must map to exactly one of:
- an explicit capability, or
- a default-authenticated classification
Examples:
pullzone.readpullzone.writedns.readdns.writestatistics.readshield.readshield.write
Some bunny API endpoints may not require an AccessKey in bunny itself, but White Rabbit must still require OIDC authentication for them.
These endpoints must:
- still pass through White Rabbit
- still require a valid authenticated session or access token
- not require an explicitly granted capability
- be marked internally as
RequiresAuthenticationOnly = true
Examples may include read-only or public-ish bunny endpoints, if White Rabbit chooses to expose them.
If an endpoint is not in the registry, White Rabbit must reject it.
Recommended response:
404 Not Foundfor unsupported proxy routes, or403 Forbiddenfor supported routes without permission
Be consistent.
White Rabbit must contain a hard-coded operation registry describing every supported proxied bunny operation.
This registry is the source of truth for:
- which routes are supported
- which HTTP methods are allowed
- which bunny destination should be called
- whether bunny credentials are required
- which capability is required, if any
- how the consent screen should describe the operation
Suggested C# record:
public sealed record BunnyOperation(
string OperationId,
string IncomingMethod,
string IncomingPathTemplate,
string DestinationBaseUrl,
string DestinationPathTemplate,
BunnyCredentialKind CredentialKind,
string? RequiredCapability,
bool RequiresAuthenticationOnly,
string ConsentTitle,
string ConsentDescription);Suggested enum:
public enum BunnyCredentialKind
{
None,
AccountApiKey,
StorageZonePassword,
StreamApiKey
}Rules:
RequiredCapabilityandRequiresAuthenticationOnlymust not both be empty or false- if
CredentialKindis notNone, White Rabbit injects the correct credential server side - operation IDs must be stable
- incoming path templates should be owned by White Rabbit, not copied blindly from bunny
- destination path templates map from White Rabbit route parameters to bunny route parameters
GET /proxy/core/pullzone -> capability: pullzone.read
POST /proxy/core/pullzone -> capability: pullzone.write
GET /proxy/core/statistics -> capability: statistics.read
GET /proxy/core/region -> auth only
GET /proxy/stream/library/{libraryId} -> capability: stream.read
GET /proxy/storage/{zoneName}/{**path} -> capability: storage.read
Note: these examples are illustrative. Final mapping should be based on the selected supported bunny endpoints.
Yes, but only as a supporting input, not as the sole source of runtime security decisions.
The bunny.net OpenAPI specifications may be used to:
- generate an initial list of candidate endpoints
- help generate DTOs or HTTP client code for selected APIs
- generate tests that verify known routes exist in the spec
- generate documentation snippets
- help detect when bunny adds, removes or changes endpoints
The OpenAPI specifications must not be the only runtime source for permission decisions.
Reason:
- White Rabbit must stay opinionated and explicit
- a generated route should not become proxyable automatically
- the security model must be controlled by White Rabbit's own operation registry
Use a build-time or developer-time tool that:
- downloads or reads selected bunny OpenAPI specs
- produces a reviewable draft list of operations
- lets a developer assign each operation one of:
- capability
- auth only
- unsupported
- outputs a strongly typed C# registry file or JSON file committed to source control
This keeps security deterministic.
Version 1 should use YARP inside the ASP.NET Core application.
YARP should be used as the forwarding engine, not as the owner of the security model.
White Rabbit code must handle:
- route resolution
- operation lookup
- capability check
- auth-only check
- destination selection
- credential injection
- audit log writing
YARP should handle:
- forwarding the HTTP request
- streaming the response back
- low-level proxying concerns
Suggested flow per request:
- authenticate request
- resolve operation from registry
- reject if unsupported
- check auth-only or capability requirement
- construct target URI
- copy selected headers
- remove unsafe headers
- inject bunny
AccessKeyif required - forward request with YARP
- log audit result
Never forward client-supplied sensitive headers directly unless explicitly intended.
At minimum:
- strip incoming
AccessKey - strip incoming
Authorizationbefore outgoing bunny call unless there is a specific future reason not to - strip any
X-Remote-*headers on routes related to Dex authproxy - define an allowlist for forwarded headers where practical
White Rabbit must support at least these credential types:
- account API key
- stream API key
- storage zone password
Requirements:
- credentials must never be returned to the client
- credentials must be read from environment variables or mounted secrets
- credentials must not be hard-coded in source control
- logging must never print credential values
- the operation registry decides which credential kind to use
Suggested environment variable names:
WHITE_RABBIT_BUNNY_ACCOUNT_API_KEY
WHITE_RABBIT_BUNNY_STREAM_API_KEY
WHITE_RABBIT_BUNNY_STORAGE_PASSWORD
If more than one stream library or storage zone later needs distinct credentials, this can be redesigned in a future version.
When a client requests access, White Rabbit must show:
- client name
- requested capabilities
- human-readable description of each capability
- any auth-only access that is always included
- approve or deny action
A grant represents that:
- a user approved a client
- for a set of capabilities
- at a specific time
A v1 grant may be indefinite until revoked.
Endpoints marked RequiresAuthenticationOnly = true:
- do not need explicit capability grant
- should still be shown as part of the behaviour of the application, either on the consent page or in documentation, so the user is not surprised
Recommended wording on consent page:
- "Basic authenticated access"
- "These operations require login but do not require extra delegated capability approval"
White Rabbit uses SQLite.
Suggested tables:
Id
Subject
Email
DisplayName
CreatedUtc
LastLoginUtc
Id
ClientId
ClientName
IsActive
CreatedUtc
Id
Name
DisplayName
Description
CreatedUtc
Id
UserId
ClientId
CreatedUtc
RevokedUtc
GrantId
CapabilityId
Id
Email
CodeHash
CreatedUtc
ExpiresUtc
ConsumedUtc
RequestIp
Id
OccurredUtc
UserId
ClientId
OperationId
RequestMethod
RequestPath
DestinationHost
DestinationPath
Outcome
HttpStatusCode
ErrorCode
Optional key/value table for simple app settings.
Minimum pages:
/login/login/verify/consent/error
Optional pages:
/grants/grants/{id}/admin/clients/admin/capabilities
Examples:
POST /auth/email/request-code
POST /auth/email/verify-code
GET /consent
POST /consent/approve
POST /consent/deny
ANY /proxy/{apiFamily}/{**path}
GET /.well-known/health
GET /.well-known/ready
The final proxy routing structure may differ, but should remain explicit and simple.
Configuration should come from:
appsettings.jsonappsettings.Development.json- environment variables
- mounted secret files if desired later
Required configuration areas:
- Dex issuer URL
- Dex client settings for White Rabbit
- SMTP settings
- database connection string for SQLite
- bunny credentials
- login code expiry
- rate limiting thresholds
- base URLs for supported bunny APIs
Suggested base URLs:
https://api.bunny.net
https://video.bunnycdn.com
https://storage.bunnycdn.com
Log at least:
- startup configuration summary without secrets
- login code requests
- login code verification results
- consent approvals and denials
- proxy decisions
- upstream bunny status codes
- unexpected exceptions
Every proxied request should create an audit event containing:
- user
- client
- operation
- destination
- result
Provide:
- liveness endpoint
- readiness endpoint
Readiness should verify:
- SQLite available
- configuration loaded
- Dex connectivity if appropriate
Do not make readiness depend on live bunny API calls unless deliberately chosen.
Mandatory security rules:
- deny by default
- never expose bunny credentials to clients
- rate limit login code requests
- rate limit code verification attempts
- expire login codes quickly
- hash login codes if practical
- strip dangerous headers
- validate redirect and callback behaviour carefully
- log security-relevant events
- do not auto-proxy newly discovered bunny endpoints
Recommended additional protections:
- CSRF protection on web forms
- secure cookies
- same-site cookie configuration
- strict HTTPS in production
- input validation on all route parameters
Must cover:
- operation registry lookup
- capability checks
- auth-only route checks
- login code generation and validation
- consent decision logic
- credential selection logic
Must cover:
- request code flow
- verify code flow
- Dex authproxy hand-off
- approval flow
- allowed proxy request
- denied proxy request
- unsupported route request
- audit event creation
Recommended:
- test that every operation in the registry maps to an expected bunny path
- optionally compare selected operations against bunny OpenAPI specs to detect drift
- create solution and projects
- add SQLite access through Activout.DatabaseClient
- implement login code flow
- integrate Dex authproxy
- implement simple consent UI
- add grant persistence
- create operation registry
- add capability check middleware or service
- add YARP forwarding
- add audit logging
- add Docker Compose
- add container images for Magic Containers deployment
Do not start with:
- automatic OpenAPI-driven proxy generation
- generic proxy configuration
- OpenFGA
- resource scoping
- complex admin UI
Suggested starter capability list:
pullzone.read
pullzone.write
dns.read
dns.write
statistics.read
stream.read
stream.write
storage.read
storage.write
shield.read
shield.write
This list may change, but it is a useful starting point.
A small and safe first slice is recommended.
Suggested first slice:
- read-only region list
- read-only statistics endpoints
- pull zone read operations
- pull zone write operations only after the read path works
- a small number of stream read operations
- avoid storage write and destructive operations until the proxy flow is proven stable
- which exact SMTP provider will be used in production?
- should grants be revocable from a user-facing page in v1?
- should the app support only confidential OIDC clients in v1?
- which bunny API families are in scope for the first release?
- should auth-only endpoints be shown on consent UI or just documented?
- should Dex persistence be durable in production, or can it be rebuilt from config for v1?
Version 1 is done when:
- a user can sign in via emailed code
- Dex issues OIDC tokens through the authproxy-based login flow
- a client can request capabilities
- White Rabbit stores grants in SQLite
- at least a small set of bunny endpoints can be proxied successfully
- unsupported routes are denied
- capability-protected routes are enforced
- auth-only routes still require OIDC
- audit events are written
- the app runs locally with Docker Compose
- the app can be deployed as a multi-container bunny.net Magic Containers application
Use the bunny OpenAPI files as a developer productivity tool, not as a runtime authority.
Practical recommendation:
- keep a hand-maintained
BunnyOperationRegistry - optionally build a small internal generator that reads bunny OpenAPI documents and proposes operation entries
- require code review for every new proxied operation
- never make "present in OpenAPI" equal "automatically allowed"
That keeps the security model simple and safe.
For version 1, Nordray White Rabbit should be:
- a .NET application called
Nordray.WhiteRabbit - backed by SQLite
- fronted by Dex for OIDC
- using White Rabbit's own email code login in front of Dex authproxy
- using YARP for forwarding
- using a hard-coded operation registry
- using capability-based grants plus auth-only routes
- deployable locally with Docker Compose
- deployable to bunny.net Magic Containers as a single application with multiple containers
This is deliberately narrow and explicit. That is a feature, not a limitation.