aic-agent — Quickstart (EN)EN
aic-agent quickstart
Three ways to talk to an AIC-protected API, from simplest to full-flow. The
examples assume a running aic-verifier service; the pairing commands are in
examples.md.
0. Install
go get github.com/varwof/aic-agent@latest
Requires Go 1.26+. Builds against published types and register; no local
replace directives.
1. Locally-minted Bearer AIC-JWT (simplest)
The agent key mints a fresh aic+jwt per request — no issuer, no user signer,
nothing but a key and a config:
package main
import (
"log"
aicagent "github.com/varwof/aic-agent"
"github.com/varwof/aic-agent/identity"
"github.com/varwof/types/aicjwt"
)
func main() {
key, err := identity.GenerateKey(identity.ES256) // persist as agent-key.pem
if err != nil {
log.Fatal(err)
}
agent, err := aicagent.New(aicagent.Config{
Mode: aicagent.Bearer,
Key: key,
Issuer: "example-agent",
Audience: []string{"myapi"},
Subject: "agent-001",
Realm: "example",
ID: "agent-001",
Capabilities: []aicjwt.Capability{
{Scheme: "demo", ID: "api:read"},
},
})
if err != nil {
log.Fatal(err)
}
defer agent.Close()
resp, err := agent.Get("https://api.example/myapi")
_ = resp
}
The token is minted per request (the verifier treats each jti as
single-use by default). Token() still returns a cached token for inspection;
the transport uses the per-request mint path.
2. mTLS client certificate
The agent presents a client certificate that carries the AIC X.509 extension:
agent, err := aicagent.New(aicagent.Config{
Mode: aicagent.MTLS,
CertFile: "client-cert.pem", // carries the AIC extension
KeyFile: "client-key.pem",
ServerCA: "ca-cert.pem", // service CA; empty = system roots
})
resp, err := agent.Get("https://api.example/myapi")
3. Remote issuance via RFC 7523 exchange
The agent holds a DA assertion (minted by bearer.SignDA with the principal's
key, or fetched from the user signer) and exchanges it at an issuer token
endpoint for a short-lived token:
agent, err := aicagent.New(aicagent.Config{
Mode: aicagent.Bearer,
RemoteIssuer: issuerURL, // token endpoint (HTTPS required)
DA: daAssertion, // or leave empty: fetched from UserSigner
Audience: []string{"myapi"},
Subject: "agent-001",
Realm: "example",
ID: "agent-001",
})
resp, err := agent.Get("https://api.example/myapi")
Tokens are fetched from the issuer and auto-refreshed on 401 (idempotent requests only; see api.md).
4. AIC issuance through the user signer (when the agent has no AIC yet)
Two-tier flow: submit a request, a human approves in the console, and core
mints the AIC certificate for a key the agent generated locally — the agent
never holds the principal's private key:
key, _ := identity.GenerateKey(identity.ES256)
agent, _ := aicagent.New(aicagent.Config{Mode: aicagent.Bearer, Key: key})
res, err := agent.ObtainAIC(ctx, aicagent.AICRequestOptions{
SignerURL: "https://127.0.0.1:8444", // user-signer mTLS
CoreURL: "https://127.0.0.1:4433", // varwof core
AgentID: "spiffe://example.com/agent/ci-bot",
PrincipalUID: "pki:alice@example.com",
Capabilities: []aicagent.AICCapability{
{SchemeID: "varwof/demo-mysql-v1", CapabilityID: "SELECT:*"},
},
ReasonCode: "operator-request",
Description: "CI bot needs one-time DB read access",
LifetimeSec: 900,
})
// res.CertPEM is the issued AIC certificate
ObtainAIC = SubmitAICRequest → WaitForAIC → GenerateCSR →
IssueAICCertificate; each step is exported for interleaved logic.
5. Handling a refusal with a challenge
Non-2xx answers parse into a *RefusalError. To close an evidence-required
denial, configure the EvidenceProvider — the SDK waits out Retry-After,
asks your provider whether the facts now exist at the verifier's gate, and
retries the byte-identical request once:
agent, _ := aicagent.New(aicagent.Config{
Token: "...",
EvidenceProvider: func(ctx context.Context, ref *aicagent.RefusalError) (bool, error) {
return deploymentHasEvidenceFacts(ref.Challenge), nil
},
})
A second denial, a stale challenge, an unavailable provider or a declined signal all reach the caller unchanged — never a repeated hammering.
Next
- Full API reference: api.md
- All config fields and shapes: reference.md
- Run it with the verifier examples: examples.md