delegated-redis 0.2.2

Redis-backed trust state for delegated capability evaluation
Documentation
# delegated-redis operations guide

This guide covers production operation of [`RedisTrustState`](https://docs.rs/delegated-redis/latest/delegated_redis/struct.RedisTrustState.html),
the Redis adapter for [`delegated`](https://docs.rs/delegated) trust-state traits.

## Role in a deployment

`delegated` evaluates signed capabilities but does not persist mutable security
state. Every multi-instance verifier must share:

- atomic nonce consumption (replay protection)
- token revocation flags
- emergency agent-deny flags

`RedisTrustState` implements both [`TrustStateStore`](https://docs.rs/delegated/latest/delegated/trait.TrustStateStore.html)
and [`TrustStateAdmin`](https://docs.rs/delegated/latest/delegated/trait.TrustStateAdmin.html)
against a single Redis deployment reachable from all evaluators.

## Key layout

All keys use the configured prefix (default `delegated`):

| Category | Redis key pattern | Value | TTL |
|---|---|---|---|
| Nonce | `{prefix}:nonce:{issuer}:{nonce}` | `1` | `max(1s, token_expiry - now)` |
| Revocation | `{prefix}:revoked:{issuer}:{token_id}` | `1` | none |
| Emergency deny | `{prefix}:denied:{issuer}:{agent_id}` | `1` | none |

Examples with the default prefix:

```text
delegated:nonce:https://issuer.example:nonce-abc123
delegated:revoked:https://issuer.example:token-42
delegated:denied:https://issuer.example:agent:scheduler
```

Issuer, token, agent, and nonce strings are used verbatim. They must match the
identifiers present in evaluated tokens.

### Prefix selection

Use `RedisTrustState::with_prefix` when multiple services or environments share
one Redis cluster:

```rust
let state = RedisTrustState::with_prefix("redis://redis.internal:6379", "prod-calendar-api")?;
```

Choose one prefix per trust domain. Do not reuse prefixes across unrelated
authorization surfaces.

## Nonce semantics

Nonce consumption uses `SET key 1 NX EX ttl`:

- **Atomic:** concurrent evaluators receive at most one successful consumption.
- **TTL-bound:** keys expire automatically when the token would no longer be valid.
- **Fail-closed:** Redis errors propagate as `TrustStateError` and deny evaluation.

The evaluator calls `consume_nonce` only after signature verification, lifetime
checks, revocation checks, and operation binding succeed. Unauthorized requests
cannot burn a valid nonce.

### TTL cleanup

Nonce keys self-expire via Redis `EX`. No sweeper job is required for nonces.

If clocks drift between evaluators and issuers, rely on the evaluator's configured
clock leeway in the core crate; Redis TTL is derived from the host-supplied
`observed_at` and token `expires_at`.

## Revocation and deny lifecycle

Revocation and emergency-deny keys are written with unconditional `SET` and have
**no TTL**. They persist until an operator deletes the key or runs an explicit
cleanup procedure.

| Action | When to use |
|---|---|
| `revoke_token(issuer, token_id)` | A specific capability must stop working immediately |
| `deny_agent(issuer, agent_id)` | An agent identity is compromised or suspended |

To clear a revocation or deny entry:

```bash
redis-cli DEL "delegated:revoked:https://issuer.example:token-42"
redis-cli DEL "delegated:denied:https://issuer.example:agent:scheduler"
```

Document your incident runbooks with the prefix and identifier conventions your
deployment uses.

## Connection URLs

`RedisTrustState` accepts standard Redis URLs understood by the `redis` crate:

```text
redis://127.0.0.1:6379
redis://:password@redis.internal:6379/0
rediss://user:password@redis.example:6380/0
```

### Standalone

Point every evaluator at the same logical database index. Use TLS (`rediss://`)
when traffic crosses untrusted networks.

### Sentinel

Use the Sentinel URL form supported by `redis` 0.27:

```text
redis+sentinel://sentinel-1:26379,sentinel-2:26379/mymaster/0
```

Verify failover behavior in staging: evaluators should reconnect through the
client and continue enforcing nonce atomicity against the promoted primary.

### Cluster

Cluster mode is supported when the URL targets a cluster-aware endpoint:

```text
redis://cluster-node:6379
```

All nonce, revocation, and deny keys for a given operation hash to the same slot
only when issuer and identifier strings collide across categories—they do not.
No multi-key transactions are required because each operation touches a single key.

Test concurrent evaluation from multiple application nodes against your cluster
topology before production cutover.

## Capacity and monitoring

Monitor:

- Redis command latency for `SET`, `EXISTS`, and connection errors
- Evaluator denials at stage `trust_state`
- Evaluator denials at stage `replay`
- Key count growth under the configured prefix
- Memory usage and eviction policy (avoid `allkeys-lru` for this use case)

Nonce volume scales with successful authorizations. Revocation/deny keys grow with
incident response activity and require manual or scripted cleanup.

## Failure behavior

Redis timeouts and connection failures return `TrustStateError`. The core
evaluator treats backend errors as **denial**. Do not bypass fail-closed
behavior during outages.

Recommended practices:

- Set client-side timeouts at the infrastructure layer
- Alert on sustained `trust_state` denials
- Run Redis with persistence appropriate to your recovery objectives (nonce loss
  on full data loss can allow replay of otherwise valid tokens until expiry)

## Local development

The reference stack in `examples/reference-stack/` uses:

```bash
docker compose up -d
export REDIS_URL=redis://127.0.0.1:6379
```

Integration tests in this crate run against Redis when `REDIS_URL` is set.

## Related documentation

- [Integration guide (Redis section)]https://github.com/abendrothj/delegated/blob/main/docs/INTEGRATION.md#redis-trust-state-delegated-redis
- [Core operations guide]https://github.com/abendrothj/delegated/blob/main/docs/OPERATIONS.md
- [Threat model]https://github.com/abendrothj/delegated/blob/main/docs/THREAT_MODEL.md