Skip to content

Commit c2d2cb6

Browse files
committed
docs(connectrpc): document the ConnectRPC adapter module
Add the connectrpc/ module to the workspace layout tables, the dependency policy, the README module list, the CHANGELOG, and the OTel span catalog (connectrpcsec.Authenticate / connectrpcsec.Authorize). grpc/go.mod picks up the workspace-aligned google.golang.org/protobuf v1.36.11 via go work sync.
1 parent 4825afa commit c2d2cb6

8 files changed

Lines changed: 52 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ legacy packages (`authentication/`, `authorization/`, the in-tree
3030
- **gRPC adapter** (`grpcsec`): unary and stream server interceptors,
3131
`UnaryAuthorize`/`StreamAuthorize`, a `metadata.MD` carrier, and an
3232
`ErrorMapper` to `codes.Code`.
33+
- **ConnectRPC adapter** (`connectrpcsec`): `NewAuthenticationInterceptor`
34+
and `NewAuthorizationInterceptor` returning `connect.Interceptor` values
35+
(unary + streaming), an `http.Header` carrier, and an `ErrorMapper` to
36+
`connect.Code`.
3337
- **Schemes**: `basic` (HTTP Basic extractor + authenticator) and `bearer`
3438
(Bearer extractor + pluggable `TokenVerifier`).
3539
- **Password hashing** (`password`): `Hasher` interface with bcrypt and
@@ -56,7 +60,7 @@ legacy packages (`authentication/`, `authorization/`, the in-tree
5660
rotation, a `Manager` (Login/Get/Touch/Rotate/Logout), and a
5761
synchronizer-token CSRF helper.
5862
- **Observability**: OpenTelemetry spans emitted directly by the core,
59-
`httpsec`, `grpcsec`, `jwtsec`, and `session`. See
63+
`httpsec`, `grpcsec`, `connectrpcsec`, `jwtsec`, and `session`. See
6064
[docs/observability.md](docs/observability.md).
6165
- **Documentation**: `docs/architecture.md`, `docs/observability.md`,
6266
`docs/security-considerations.md`, `docs/migration-from-v0.md`, and a

CLAUDE.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ is intentionally skipped in CI.
8484
| `.` | `security` | Core transport-agnostic primitives |
8585
| `./http` | `.../http``httpsec` | `net/http` adapter (middleware, `Authorize`, carrier) |
8686
| `./grpc` | `.../grpc``grpcsec` | gRPC unary/stream interceptors + `Authorize` |
87+
| `./connectrpc` | `.../connectrpc``connectrpcsec` | ConnectRPC auth + authorize interceptors |
8788
| `./basic` | `.../basic` | HTTP Basic extractor + authenticator |
8889
| `./bearer` | `.../bearer` | Bearer extractor + `TokenVerifier` authenticator |
8990
| `./password` | `.../password` | BCrypt + Argon2id hashers (`NeedsRehash`) |
@@ -93,7 +94,7 @@ is intentionally skipped in CI.
9394
| `./oauth2/storage/memory` | `.../oauth2/storage/memory` | In-memory `oauth2.Storage`**package of `oauth2`** |
9495
| `./oauth2/store/sql` | `.../oauth2/store/sql` | Production storage on `database/sql` (PG/MySQL/SQLite) |
9596
| `./oauth2/store/redis` | `.../oauth2/store/redis` | Production storage on Redis (Lua atomicity) |
96-
| `./examples` | `.../examples` | Runnable demos: basic-http, bearer-jwt, grpc-bearer, session-web, oauth2 |
97+
| `./examples` | `.../examples` | Runnable demos: basic-http, bearer-jwt, grpc-bearer, connectrpc-bearer, session-web, oauth2 |
9798
| `./internal/integrations` | (private) | Cross-module end-to-end tests |
9899

99100
`oauth2/storage/memory` is **not** a standalone module — it is a sub-package
@@ -102,8 +103,8 @@ of `oauth2`. The other rows are independent modules (own `go.mod`).
102103
**The dependency direction is a hard rule** (enforced by review, see
103104
`MIGRATION.md`): the **core (`.`) must depend only on stdlib +
104105
`go.opentelemetry.io/otel`** (+ `testify` in its own tests). It MUST NOT
105-
import gRPC, JOSE/JWT libs, OAuth2, Redis, SQL drivers, HTTP routers, or
106-
concrete loggers. Adapters depend on the core, never the reverse. The
106+
import gRPC, ConnectRPC, JOSE/JWT libs, OAuth2, Redis, SQL drivers, HTTP
107+
routers, or concrete loggers. Adapters depend on the core, never the reverse. The
107108
`oauth2` module has **no hard dependency on `jwt`** — JWT access tokens are
108109
wired via an adapter (`jwt` depends on `oauth2`, not the other way). When
109110
adding code, check the allowed-dependency list in `MIGRATION.md` before
@@ -163,7 +164,7 @@ Conventions baked into the core:
163164
- **OTel spans live directly in each module** — there is intentionally no
164165
`EventSink` abstraction and no separate `otel/` module. The core uses
165166
scope `github.com/hyperscale-stack/security`; each instrumented module
166-
(`httpsec`, `grpcsec`, `jwtsec`, `session`) uses its own. See
167+
(`httpsec`, `grpcsec`, `connectrpcsec`, `jwtsec`, `session`) uses its own. See
167168
`docs/observability.md` for the span catalog.
168169

169170
## OAuth2 server (`oauth2/`)
@@ -187,8 +188,8 @@ configurable via `ServerConfig.RoutePrefix`).
187188
the shared `oauth2/storetest` conformance suite.
188189

189190
`examples/oauth2/main.go` is the canonical wiring example for the v2 stack;
190-
`examples/` also has `basic-http`, `bearer-jwt`, `grpc-bearer`, and
191-
`session-web` demos.
191+
`examples/` also has `basic-http`, `bearer-jwt`, `grpc-bearer`,
192+
`connectrpc-bearer`, and `session-web` demos.
192193

193194
## Tooling caveats
194195

MIGRATION.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ released on its own cadence.
1212
| `.` | `github.com/hyperscale-stack/security` | Core: transport-agnostic primitives (Authentication, Engine, Voter…) |
1313
| `./http` | `github.com/hyperscale-stack/security/http` | `httpsec``net/http` adapter |
1414
| `./grpc` | `github.com/hyperscale-stack/security/grpc` | `grpcsec` — gRPC unary/stream interceptors |
15+
| `./connectrpc` | `github.com/hyperscale-stack/security/connectrpc` | `connectrpcsec` — ConnectRPC auth + authorize interceptors |
1516
| `./basic` | `github.com/hyperscale-stack/security/basic` | HTTP Basic extractor + authenticator |
1617
| `./bearer` | `github.com/hyperscale-stack/security/bearer` | Bearer extractor + `TokenVerifier`-based authenticator |
1718
| `./password` | `github.com/hyperscale-stack/security/password` | BCrypt + Argon2id hashers |
@@ -39,6 +40,7 @@ own tests).
3940
core (.) ← stdlib + go.opentelemetry.io/otel
4041
http/ ← core + otel
4142
grpc/ ← core + otel + google.golang.org/grpc
43+
connectrpc/ ← core + otel + connectrpc.com/connect
4244
basic/ ← core + password
4345
bearer/ ← core
4446
password/ ← golang.org/x/crypto
@@ -52,8 +54,8 @@ examples/ ← may depend on every module above
5254
(`oauth2/storage/memory` is a sub-package of the `oauth2` module.)
5355
```
5456

55-
The core MUST NOT depend on: gRPC, JWT/JOSE libs, OAuth2, Redis, SQL drivers,
56-
HTTP routers, or concrete loggers. Its direct dependency set is exactly
57+
The core MUST NOT depend on: gRPC, ConnectRPC, JWT/JOSE libs, OAuth2, Redis,
58+
SQL drivers, HTTP routers, or concrete loggers. Its direct dependency set is exactly
5759
stdlib + `go.opentelemetry.io/otel` (+ `stretchr/testify` scoped to its own
5860
tests). The policy is enforced by review.
5961

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ Hyperscale security [![Last release](https://img.shields.io/github/release/hyper
88
| master | [![Build Status](https://github.com/hyperscale-stack/security/workflows/Go/badge.svg?branch=master)](https://github.com/hyperscale-stack/security/actions?query=workflow%3AGo) | [![Coveralls](https://img.shields.io/coveralls/hyperscale-stack/security/master.svg)](https://coveralls.io/github/hyperscale-stack/security?branch=master) |
99

1010
A transport-agnostic authentication and authorization toolkit for Go —
11-
HTTP and gRPC, OAuth2, JWT, sessions, and a composable Voter-based access
12-
model. It is shipped as a multi-module workspace so you import only what
13-
you need.
11+
HTTP, gRPC and ConnectRPC, OAuth2, JWT, sessions, and a composable
12+
Voter-based access model. It is shipped as a multi-module workspace so you
13+
import only what you need.
1414

1515
## Modules
1616

@@ -19,6 +19,7 @@ you need.
1919
| `github.com/hyperscale-stack/security` | Core: `Authentication`, `Engine`, `Manager`, `Voter`, ADM |
2020
| `…/security/http` | `httpsec``net/http` middleware + authorization |
2121
| `…/security/grpc` | `grpcsec` — unary/stream interceptors |
22+
| `…/security/connectrpc` | `connectrpcsec` — ConnectRPC auth + authorize interceptors |
2223
| `…/security/basic` | HTTP Basic extractor + authenticator |
2324
| `…/security/bearer` | Bearer extractor + `TokenVerifier` authenticator |
2425
| `…/security/password` | BCrypt + Argon2id hashers (`NeedsRehash`) |

docs/architecture.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ transitive dependencies.
99
## Design goals
1010

1111
- **Transport-agnostic core.** The authentication pipeline knows nothing
12-
about `net/http` or gRPC. Transports are thin adapters.
12+
about `net/http`, gRPC, or ConnectRPC. Transports are thin adapters.
1313
- **Small, immutable interfaces.** `Authentication` is read-only; state
1414
changes produce new values. No mutable `interface{}` credential bag.
1515
- **Composable authorization.** A Voter / `AccessDecisionManager` model
@@ -26,6 +26,7 @@ transitive dependencies.
2626
| `.` | `github.com/hyperscale-stack/security` | Core: `Authentication`, `Engine`, `Manager`, `Voter`, `AccessDecisionManager` |
2727
| `./http` | `…/security/http` | `httpsec``net/http` middleware + carrier |
2828
| `./grpc` | `…/security/grpc` | `grpcsec` — unary/stream interceptors + carrier |
29+
| `./connectrpc` | `…/security/connectrpc` | `connectrpcsec` — ConnectRPC auth + authorize interceptors |
2930
| `./basic` | `…/security/basic` | HTTP Basic extractor + authenticator |
3031
| `./bearer` | `…/security/bearer` | Bearer extractor + `TokenVerifier`-based authenticator |
3132
| `./password` | `…/security/password` | BCrypt + Argon2id hashers (`NeedsRehash`) |
@@ -46,6 +47,7 @@ separate module) — it ships an in-memory `oauth2.Storage` for dev and tests.
4647
core (.) ← stdlib + go.opentelemetry.io/otel
4748
http/ ← core + otel
4849
grpc/ ← core + otel + google.golang.org/grpc
50+
connectrpc/ ← core + otel + connectrpc.com/connect
4951
basic/ ← core + password
5052
bearer/ ← core
5153
password/ ← golang.org/x/crypto
@@ -57,9 +59,9 @@ oauth2/store/redis/ ← oauth2 + github.com/redis/go-redis/v9
5759
examples/ ← may depend on every module above
5860
```
5961

60-
The core MUST NOT depend on gRPC, JOSE libraries, OAuth2, Redis, SQL
61-
drivers, HTTP routers, or concrete loggers. This boundary is what keeps the
62-
core importable from any transport.
62+
The core MUST NOT depend on gRPC, ConnectRPC, JOSE libraries, OAuth2,
63+
Redis, SQL drivers, HTTP routers, or concrete loggers. This boundary is
64+
what keeps the core importable from any transport.
6365

6466
## The authentication pipeline
6567

@@ -80,7 +82,8 @@ Carrier ──▶ Extractor ──▶ Authentication (pending)
8082

8183
- **`Carrier`** abstracts a transport message — read credentials, write
8284
challenges. `httpsec.Carrier` wraps `*http.Request`/`http.ResponseWriter`;
83-
`grpcsec.Carrier` wraps `metadata.MD`.
85+
`grpcsec.Carrier` wraps `metadata.MD`; `connectrpcsec.Carrier` wraps
86+
`http.Header`.
8487
- **`Extractor`** pulls raw, unauthenticated credentials from a `Carrier`.
8588
Returns `(nil, nil)` when its scheme is absent.
8689
- **`Authenticator`** validates a pending `Authentication` and returns an
@@ -128,6 +131,10 @@ and a `Carrier`, then map security errors to transport responses.
128131
- **`grpcsec`**`UnaryServerInterceptor` / `StreamServerInterceptor`
129132
authenticate every RPC; `UnaryAuthorize` / `StreamAuthorize` enforce an
130133
ADM. `ErrorMapper` turns sentinels into `codes.Code`.
134+
- **`connectrpcsec`**`NewAuthenticationInterceptor` authenticates every
135+
RPC; `NewAuthorizationInterceptor` enforces an ADM. Both return a
136+
`connect.Interceptor` covering unary and streaming. `ErrorMapper` turns
137+
sentinels into `connect.Code`.
131138

132139
## OAuth2
133140

docs/observability.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ application; the library uses the global provider via `otel.Tracer`.
1010

1111
Each module reports under a stable instrumentation scope (the tracer name):
1212

13-
| Module | Instrumentation scope |
14-
| --------- | ---------------------------------------------- |
15-
| core | `github.com/hyperscale-stack/security` |
16-
| `httpsec` | `github.com/hyperscale-stack/security/http` |
17-
| `grpcsec` | `github.com/hyperscale-stack/security/grpc` |
18-
| `jwtsec` | `github.com/hyperscale-stack/security/jwt` |
19-
| `session` | `github.com/hyperscale-stack/security/session` |
13+
| Module | Instrumentation scope |
14+
| --------------- | ------------------------------------------------- |
15+
| core | `github.com/hyperscale-stack/security` |
16+
| `httpsec` | `github.com/hyperscale-stack/security/http` |
17+
| `grpcsec` | `github.com/hyperscale-stack/security/grpc` |
18+
| `connectrpcsec` | `github.com/hyperscale-stack/security/connectrpc` |
19+
| `jwtsec` | `github.com/hyperscale-stack/security/jwt` |
20+
| `session` | `github.com/hyperscale-stack/security/session` |
2021

2122
The `basic`, `bearer`, `password` and `oauth2` modules do not open spans of
2223
their own — keeping them free of a direct `go.opentelemetry.io/otel`
@@ -60,6 +61,16 @@ it delegates to `security.AccessDecisionManager.Decide`.
6061
`grpcsec` deliberately does **not** open an `rpc` span — that belongs to
6162
`otelgrpc`, which you compose alongside these interceptors.
6263

64+
### ConnectRPC — `github.com/hyperscale-stack/security/connectrpc`
65+
66+
| Span | When | Attributes | Error status |
67+
| ---------------------------- | ------------------------------------------ | -------------------------------------------------------------------- | ----------------------- |
68+
| `connectrpcsec.Authenticate` | Per RPC, unary and streaming interceptors | `rpc.method` (string), `security.authenticated` (bool) | inherited from the core |
69+
| `connectrpcsec.Authorize` | The authorization interceptor | none directly — delegates to `security.AccessDecisionManager.Decide` | inherited from the core |
70+
71+
`connectrpcsec` deliberately does **not** open an `rpc` span — that belongs
72+
to `otelconnect`, which you compose alongside these interceptors.
73+
6374
### JWT — `github.com/hyperscale-stack/security/jwt`
6475

6576
| Span | When | Attributes | Error status |

grpc/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
golang.org/x/sys v0.44.0 // indirect
2424
golang.org/x/text v0.37.0 // indirect
2525
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
26-
google.golang.org/protobuf v1.36.1 // indirect
26+
google.golang.org/protobuf v1.36.11 // indirect
2727
gopkg.in/yaml.v3 v3.0.1 // indirect
2828
)
2929

grpc/go.sum

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:
4545
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
4646
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
4747
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
48-
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
49-
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
48+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
5049
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5150
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
5251
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

0 commit comments

Comments
 (0)