Skip to content

feat(auth): add opt-in managed agent token issuance - #1043

Draft
intel352 wants to merge 2 commits into
mainfrom
feat/m2m-managed-token-boundary-20261009
Draft

intel352 wants to merge 2 commits into
mainfrom
feat/m2m-managed-token-boundary-20261009

Conversation

@intel352

@intel352 intel352 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Managed agent grants need a signing boundary that preserves their approved subject, tenant, grant version, audience, scopes and expiry. This adds a typed M2MAuthModule.IssueManagedToken API with fixed protected claims, explicit stable ES256 configuration, an internally generated JTI and a 15-minute lifetime cap. Managed JWT and JWKS key IDs use the existing RFC 7638 thumbprint helper.

The dedicated issuer must opt into SetManagedOnly(true), which denies generic token, revoke and introspection handlers before parsing inputs. JWKS remains readable and the default legacy issuer, expiry, provider and revocation behavior remain compatible. The API is reusable for any approved positive tenant/grant version and exact HTTPS audience; it contains no deployment-specific identities.

The reusable auth integration separately owns approved grants, current policy/version/expiry/revocation checks, exchange replay protection, strict JWT authorization and audit. This PR does not enable a managed identity or endpoint, create credentials or keys, update dependency pins, release or deploy a service.

Validation at frozen head 9971ca5067f3b6a7cafe9667e381efab62e52d1c, based on current main f043410f5dc727af7adc800e48786a31c09b3f7e:

  • Offline named-file synthetic race tests passed: signed bindings, key/algorithm/mode refusals, canonical request checks, clock/expiry limits, cancellation, generic route denial, managed/legacy JWKS IDs and legacy default issuance.
  • Command: GOWORK=off GOPROXY=off GOTOOLCHAIN=local GOFLAGS=-mod=readonly GOMAXPROCS=2 go test -race -p 2 -count=1 -v module/auth_m2m.go module/auth_m2m_managed.go module/auth_m2m_managed_test.go.
  • git diff --check passed. Full module builds/tests were not run locally; existing repository CI and independent review remain required before any release.

The five-file merge diff excludes unrelated work from the original local CLI branch. The original workspace and its .autodev state are preserved.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

⏱ Benchmark Results

⚠️ Potential performance regression detected — one or more benchmarks regressed ≥ 20%.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 9V74 80-Core Processor                
                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │       sec/op       │    sec/op     vs base               │
InterpreterCreation-4               3.013m ± 14%   2.410m ± 19%  -20.01% (p=0.002 n=6)
ComponentLoad-4                     3.525m ±  0%   2.805m ±  2%  -20.43% (p=0.002 n=6)
ComponentExecute-4                  1.859µ ±  0%   1.447µ ±  1%  -22.16% (p=0.002 n=6)
PoolContention/workers-1-4         1028.0n ±  1%   810.0n ±  9%  -21.21% (p=0.002 n=6)
PoolContention/workers-2-4         1029.0n ±  2%   806.9n ±  1%  -21.58% (p=0.002 n=6)
PoolContention/workers-4-4         1025.0n ±  1%   813.3n ±  2%  -20.65% (p=0.002 n=6)
PoolContention/workers-8-4         1032.0n ±  1%   814.6n ±  2%  -21.07% (p=0.002 n=6)
PoolContention/workers-16-4        1027.0n ±  1%   815.8n ±  1%  -20.56% (p=0.002 n=6)
ComponentLifecycle-4                3.550m ±  3%   2.887m ±  2%  -18.66% (p=0.002 n=6)
SourceValidation-4                  2.084µ ±  1%   1.642µ ±  1%  -21.23% (p=0.002 n=6)
RegistryConcurrent-4                759.6n ±  4%   596.4n ±  3%  -21.48% (p=0.002 n=6)
LoaderLoadFromString-4              3.581m ±  1%   2.918m ±  1%  -18.51% (p=0.002 n=6)
geomean                             16.66µ         13.22µ        -20.64%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.846 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.613 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.182 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.182Mi ± 0%   2.182Mi ± 0%       ~ (p=0.913 n=6)
geomean                             15.25Ki        15.25Ki       -0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.68k ± 0%   15.68k ± 0%       ~ (p=1.000 n=6) ¹
ComponentLoad-4                      18.02k ± 0%   18.02k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 18.07k ± 0%   18.07k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               18.06k ± 0%   18.06k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               183.3         183.3       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │       sec/op       │   sec/op     vs base               │
CircuitBreakerDetection-4                  298.1n ± 1%   232.2n ± 6%  -22.09% (p=0.002 n=6)
CircuitBreakerExecution_Success-4          22.68n ± 1%   17.57n ± 0%  -22.51% (p=0.002 n=6)
CircuitBreakerExecution_Failure-4          71.17n ± 0%   55.19n ± 0%  -22.45% (p=0.002 n=6)
geomean                                    78.36n        60.84n       -22.35%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │        benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base               │
IaCStateBackend_InProcess-4              297.0n ± 39%   229.4n ±  1%  -22.76% (p=0.002 n=6)
IaCStateBackend_GRPC-4                   9.711m ±  2%   7.779m ± 11%  -19.89% (p=0.002 n=6)
JQTransform_Simple-4                     639.5n ± 43%   550.2n ± 19%  -13.96% (p=0.026 n=6)
JQTransform_ObjectConstruction-4         1.524µ ±  1%   1.152µ ±  2%  -24.42% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.544µ ±  1%   2.802µ ±  4%  -20.95% (p=0.002 n=6)
JQTransform_Complex-4                    42.83µ ±  1%   32.53µ ±  1%  -24.04% (p=0.002 n=6)
JQTransform_Throughput-4                 1.855µ ±  2%   1.381µ ±  1%  -25.55% (p=0.002 n=6)
SSEPublishDelivery-4                     63.31n ±  0%   50.23n ±  0%  -20.67% (p=0.002 n=6)
geomean                                  3.868µ         3.032µ        -21.60%

                                 │ baseline-bench.txt │        benchmark-results.txt         │
                                 │        B/op        │     B/op      vs base                │
IaCStateBackend_InProcess-4             416.0 ±  0%       416.0 ± 0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                5.733Mi ± 14%     5.656Mi ± 7%       ~ (p=0.818 n=6)
JQTransform_Simple-4                  1.273Ki ±  0%     1.273Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4      1.773Ki ±  0%     1.773Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4             2.625Ki ±  0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                 16.31Ki ±  0%     16.31Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4              1.984Ki ±  0%     1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                    0.000 ±  0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                 -0.17%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │        benchmark-results.txt        │
                                 │     allocs/op      │  allocs/op   vs base                │
IaCStateBackend_InProcess-4              2.000 ± 0%      2.000 ± 0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                  6.854k ± 0%     6.859k ± 0%       ~ (p=0.329 n=6)
JQTransform_Simple-4                     10.00 ± 0%      10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%      15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%      30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    328.0 ± 0%      328.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%      17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                +0.01%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/orchestration
                 │ baseline-bench.txt │       benchmark-results.txt        │
                 │       sec/op       │   sec/op     vs base               │
SagaConcurrent-4          5.037µ ± 1%   3.845µ ± 1%  -23.66% (p=0.002 n=6)

                 │ baseline-bench.txt │       benchmark-results.txt        │
                 │        B/op        │     B/op      vs base              │
SagaConcurrent-4         1.176Ki ± 0%   1.154Ki ± 0%  -1.87% (p=0.002 n=6)

                 │ baseline-bench.txt │     benchmark-results.txt     │
                 │     allocs/op      │ allocs/op   vs base           │
SagaConcurrent-4           17.00 ± 0%   17.00 ± 0%  ~ (p=1.000 n=6) ¹
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │       sec/op       │   sec/op     vs base               │
SchemaValidation_Simple-4                   1061.0n ± 2%   822.5n ± 2%  -22.48% (p=0.002 n=6)
SchemaValidation_AllFields-4                 1.591µ ± 1%   1.231µ ± 1%  -22.63% (p=0.002 n=6)
SchemaValidation_FormatValidation-4          1.539µ ± 3%   1.195µ ± 1%  -22.36% (p=0.002 n=6)
SchemaValidation_ManySchemas-4               1.579µ ± 2%   1.225µ ± 2%  -22.42% (p=0.002 n=6)
geomean                                      1.423µ        1.103µ       -22.47%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │         benchmark-results.txt          │
                                   │       sec/op       │     sec/op      vs base                │
EventStoreAppend_InMemory-4                906.9n ± 13%    692.5n ±   2%   -23.64% (p=0.002 n=6)
EventStoreAppend_SQLite-4                  901.5µ ±  6%   3378.3µ ± 229%  +274.72% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4          10.032µ ±  3%    8.384µ ±   1%   -16.43% (p=0.002 n=6)
GetTimeline_InMemory/events-50-4           55.40µ ±  1%    46.36µ ±   2%   -16.31% (p=0.002 n=6)
GetTimeline_InMemory/events-100-4         110.95µ ±  1%    92.67µ ±   2%   -16.47% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          569.9µ ±  3%    477.9µ ±   2%   -16.14% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4        1154.5µ ±  1%    980.1µ ±   1%   -15.10% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             55.51µ ±  1%    46.52µ ±   1%   -16.20% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             180.1µ ±  1%    147.7µ ±   2%   -17.98% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            332.1µ ±  2%    277.4µ ±   3%   -16.45% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.558m ±  1%    1.276m ±   2%   -18.08% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.078m ±  2%    2.522m ±   2%   -18.07% (p=0.002 n=6)
geomean                                    168.6µ          158.0µ           -6.29%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                 752.5 ± 14%     819.0 ±  6%       ~ (p=0.240 n=6)
EventStoreAppend_SQLite-4                 2.054Ki ±  1%   2.059Ki ± 26%       ~ (p=0.554 n=6)
GetTimeline_InMemory/events-10-4          7.953Ki ±  0%   7.953Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4          46.62Ki ±  0%   46.62Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4         94.48Ki ±  0%   94.48Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4         472.8Ki ±  0%   472.8Ki ±  0%       ~ (p=1.000 n=6)
GetTimeline_InMemory/events-1000-4        944.3Ki ±  0%   944.3Ki ±  0%       ~ (p=0.061 n=6)
GetTimeline_SQLite/events-10-4            15.59Ki ±  0%   15.59Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4            80.56Ki ±  0%   80.56Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4           162.1Ki ±  0%   162.1Ki ±  0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4           777.4Ki ±  0%   777.4Ki ±  0%  -0.00% (p=0.032 n=6)
GetTimeline_SQLite/events-1000-4          1.503Mi ±  0%   1.503Mi ±  0%  -0.00% (p=0.015 n=6)
geomean                                   65.06Ki         65.53Ki        +0.73%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    55.00 ± 0%    55.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               316.0 ± 0%    316.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.486k ± 0%   1.486k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             2.940k ± 0%   2.940k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             14.79k ± 0%   14.79k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            29.80k ± 0%   29.80k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.065k        1.065k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant