Skip to content

fix(go): start the doc comments after the optional loadAgentCard on their own line - #485

Merged
edenreich merged 1 commit into
mainfrom
fix/go-template-root-cmd-doc
Oct 5, 2026
Merged

edenreich merged 1 commit into
mainfrom
fix/go-template-root-cmd-doc

Conversation

@edenreich

Copy link
Copy Markdown
Contributor

Summary

The generated Go main.go runs a doc comment into the closing brace of the function above it, so godoc no longer attaches the comment to its function:

Manifest Generated before Generated now
no extendedAgentCard } // newRootCmd builds... }, blank line, // newRootCmd builds...
extendedAgentCard: true }// loadAgentCard reads... (gofmt: } // loadAgentCard reads...) }, blank line, // loadAgentCard reads...

The {{- if .ADL.Spec.Capabilities.ExtendedAgentCard }} action trimmed the blank line before it. Trimming after the action instead ({{ if ... -}}) keeps the blank line in both layouts.

The bug predates #483, which only moved it from taskWorkers to advertiseAuthScheme. inference-gateway/mock-agent fixes the line by hand in its hand-maintained main.go (see inference-gateway/mock-agent#172).

Testing

  • New TestGoMainTemplate_DocCommentsStartOnTheirOwnLine renders main.go with and without the extended agent card. It fails on main and passes with the fix.
  • Generated the mock-agent manifest with a local build, with and without extendedAgentCard: true. Neither main.go has a } // line, and gofmt -l reports nothing.
  • task lint (0 issues) and task test pass.

…heir own line

The {{- if ExtendedAgentCard }} action trimmed the blank line before it, so
the generated main.go ran the next doc comment into the closing brace of the
function above. Without the extended agent card that was
`} // newRootCmd builds...`, with it `}// loadAgentCard reads...`, which
gofmt leaves as a trailing comment and godoc no longer attaches to the
function.

Trim after the action instead, so both layouts keep a blank line before
each doc comment.
@edenreich
edenreich requested a review from a team as a code owner October 5, 2026 16:16
@edenreich
edenreich merged commit e03f35f into main Oct 5, 2026
5 checks passed
@edenreich
edenreich deleted the fix/go-template-root-cmd-doc branch October 5, 2026 16:18
inference-gateway-releaser Bot added a commit that referenced this pull request Oct 5, 2026
## [0.67.2](v0.67.1...v0.67.2) (2026-10-05)

### 🐛 Bug Fixes

* **go:** start the doc comments after the optional loadAgentCard on their own line ([#485](#485)) ([e03f35f](e03f35f))
@inference-gateway-releaser

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.67.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

edenreich added a commit to inference-gateway/cli that referenced this pull request Oct 5, 2026
… a2a-auth example (#1542)

## Summary

Lets `infer` authenticate to A2A agents that protect `/a2a`. An agent
entry in `agents.yaml` can carry an `auth` block with a static bearer
token or an OIDC client-credentials grant.

Closes #1514 and closes #1512, which adds `examples/a2a-auth` on top of
it.

```yaml
agents:
  - name: research
    url: https://research.example.com
    auth:
      token_env: RESEARCH_AGENT_TOKEN
  - name: billing
    url: https://billing.example.com
    auth:
      oidc:
        client_id: infer
        client_secret_env: BILLING_AGENT_CLIENT_SECRET
        audience: billing-agent
```

## How it works

- **Secrets stay in the environment.** The `auth` block names
environment variables and never holds a value. `LoadYAML` expands
`${VAR}` over the whole file and every `infer agents add/update/remove`
writes the result back, so a `token: ${VAR}` field would have ended up
on disk in plain text. With variable names, the file, `infer agents
list` and `infer agents show` cannot leak a secret.
- **Every request is covered.** The credentials are added by a transport
inside `a2ainfra.NewClient`, the one constructor behind the agent card
fetch, `SendMessage`, `GetTask` polling, `CancelTask`, the liveness
probe and `infer agents status`. ADK `Config.Headers` does not reach the
card fetch, a transport does.
- **Credentials stay on their agent.** `agent_url` comes from the model,
so the transport only authenticates requests to the scheme, host and
port of the configured agent. A redirect target or another port on the
same host gets nothing.
- **The agent card says where tokens come from.** As the A2A protocol
defines, the agent declares how to authenticate in the `securitySchemes`
of its card. The CLI reads the token endpoint from there: the discovery
URL of an `openIdConnect` scheme, or the token URL of an `oauth2`
client-credentials flow, with the scopes the card's
`securityRequirements` list. `agents.yaml` only holds the client id, the
secret variable and an optional audience.
- **Optional `issuer_url`, pin and fallback.** The client secret goes to
whatever endpoint the card names, and the card is fetched
unauthenticated. With `auth.oidc.issuer_url` set, a card that points
outside that issuer is refused before the secret is sent. The same field
is the fallback for a card that declares no scheme at all, as agents
generated before ADL CLI v0.67.0 do.
- **OIDC tokens are reused.** One `clientcredentials` token source per
grant lives for the process, fetches once and refreshes shortly before
expiry. This adds `golang.org/x/oauth2` as a direct dependency.
- **Auth failures are named.** A 401 or 403, a missing variable or a
failed token request reaches the model as `Authentication failed for A2A
agent "<name>": ...`, without the response body. Background polling ends
the job on it instead of retrying forever.

## Example (#1512)

`examples/a2a-auth/` runs one `mock-agent` 0.5.0 behind `A2A_AUTH_TOKEN`
and one behind OIDC against Keycloak, with a tokenless mock model and no
provider API key. The credentials are in the example's
`.infer/agents.yaml`, with the secrets read from the environment. Each
agent declares its scheme on its card, so the OIDC entry only holds the
client and `infer` reads the token endpoint from the card.

| Task | Result |
| --- | --- |
| `task token` | delegation to the token agent completes |
| `task oidc` | delegation to the OIDC agent completes with a token from
Keycloak |
| `task wrong-token` | `Authentication failed for A2A agent
"token-agent": the agent rejected the request with status 401` |
| `task missing-token` | `Authentication failed for A2A agent
"token-agent": environment variable TOKEN_AGENT_TOKEN is not set` |
| `task wrong-secret` | `Authentication failed for A2A agent
"oidc-agent": fetching the OIDC token: the token endpoint answered with
status 401 unauthorized_client` |

Keycloak and the OIDC agent share a network namespace, so the issuer is
`http://localhost:8090/realms/a2a` for `infer` on the host and for the
agent in its container. `docs/examples.md` lists the example and
`docs/agents-configuration.md` links it.

## Testing

- `infrastructure/auth_test.go`: the header on the card fetch,
`SendTask`, `GetTask` and `CancelTask`, no header on another origin or
after a cross-origin redirect, the OIDC token endpoint read from an
`openIdConnect` and an `oauth2` card scheme, one token fetch across
requests plus a refetch once the token expires, a card with no usable
scheme or outside the pinned issuer refused before the secret is sent,
the auth-failure message for 401 and 403, an unset variable.
- `config/agents_test.go`: origin matching and a load and rewrite of
`agents.yaml` that never contains the secret.
- `submit_task_error_handling_test.go`: a rejected submission and a
rejected poll both end with the named error.
- Manual: all five example scenarios above and `task status` run against
`mock-agent` 0.5.0 and Keycloak 26.3.2 with a local build, with no
`issuer_url` configured.
- `task precommit:run` passes.

## Not in this PR

- `infer agents add/update` flags for `auth`. Edit `agents.yaml`
directly for now.
- Credentials for an agent that is only in `INFER_A2A_AGENTS` with no
`agents.yaml` entry.
- Authentication on artifact downloads.
- ADK v0.33.0 has no typed HTTP error, so a 401 or 403 is recognised
from the error text. A test pins the wording. A typed error in
`inference-gateway/adk` would remove the text match and the roughly 6
second retry before a credential error surfaces on task requests.

## Affected repos

- `inference-gateway/docs` needs the new `auth` block documented,
tracked in inference-gateway/docs#985.
- `inference-gateway/adl-cli`: generated Go agents did not declare their
runtime auth mode in the card's `securitySchemes`. Fixed in
inference-gateway/adl-cli#483 (closes inference-gateway/adl-cli#482),
released in v0.67.0.
- `inference-gateway/mock-agent`: its hand-maintained `main.go` was
re-synced with the template in inference-gateway/mock-agent#172,
released in 0.5.0, which the example pins.
- inference-gateway/adl-cli#485 fixes an older template bug found during
that sync, a doc comment glued onto the closing brace above it. It does
not affect this PR.
- `inference-gateway/adk` could expose a typed HTTP status error
(optional follow-up, see above).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant