aic-agent — API (EN)EN
aic-agent API reference
The complete public surface. Everything below is in package aicagent
(github.com/varwof/aic-agent); subpackages identity, bearer and
transport are covered at the end. go doc github.com/varwof/aic-agent is the
exhaustive source; this is the curated copy.
Symbols
| Call | Use |
|---|---|
New(cfg Config) (*Agent, error) |
build an agent; returns an error on an invalid Config |
LoadClientConfigFile(path) / ParseClientConfig(data) |
configure from a JSON file (unknown fields rejected) |
(c Config) Validate() error |
validate a config standalone |
(c Config) Authorize(opID, params) |
CLC pre-check against c.Capabilities |
AuthorizeGrants(grants, opID, params) |
CLC pre-check against an explicit grant set |
identity.GenerateKey / Load / ParsePEM |
create/load an agent key (ES256/EdDSA, P-256/Ed25519) |
bearer.Sign(key, opt) |
mint a local aic+jwt |
bearer.SignDA(opt) |
sign a Delegation Authorization assertion (v2, SPKI-bound by default) |
(IssuerClient) Exchange(ctx, da, audience) |
RFC 7523 exchange at a token endpoint |
Agent lifecycle
| Method | Behaviour |
|---|---|
New(cfg) |
validates config, loads key/certs, builds transports |
Close() error |
releases config-owned resources (log file, etc.) |
(a Agent) Client() *http.Client |
the underlying client for code that wants the standard surface |
(a Agent) Token() (string, error) |
the current bearer token (cached form; local mode mints per request at the transport) |
(a Agent) Refresh() (string, error) |
force a fresh token (import for remote issuance) |
(a Agent) Get(uri) / (a Agent) Do(req) |
one-shot calls on the credential-carrying transport |
(a Agent) DoWithOptions(req, opt) |
Do with per-request options |
(a Agent) DoAIC(req, opt) (*DoResult, error) |
the structured call: returns the parsed refusal and evidence-retry state |
DoAIC returns a DoResult carrying Denied *RefusalError,
EvidenceClosed bool and EvidenceRefused bool so callers can distinguish a
plain 403 from "the evidence machine retried once" vs "a live challenge went
unanswered".
AIC issuance (human-in-the-loop)
| Method | Behaviour |
|---|---|
SubmitAICRequest(ctx, opt) (id string, err) |
enqueue the request; returns its id |
WaitForAIC(ctx, opt, id, timeout, poll) |
poll until a human approves/rejects |
GenerateCSR(key, cn) |
build the CSR for the agent's own key |
IssueAICCertificate(ctx, opt, status, csrPEM) (*AICResult) |
present evidence + CSR to core; mints the cert |
ObtainAIC(ctx, opt) (*AICResult) |
all four in sequence |
AICResult carries CertPEM, KeyPEM, Serial, CA, RequestID,
UserCertPEM and the effective Constraints. AICRequestOptions holds
SignerURL, CoreURL, CoreToken, AgentID, PrincipalUID,
Capabilities, ReasonCode, Description, LifetimeSec, DAVersion (2 =
SPKI-bound, default; 1 = legacy), ValidityDays, Subject.
Refusals and challenges
RefusalError is the SDK's signature error (matchable with errors.As):
type RefusalError struct {
Method, URL string
Status int
Code string // compact {"code":…}
Message string
// RFC 9457 problem members
ProblemType, Title, Detail string
Challenge *semantics.Challenge // parsed CLC-CHALLENGE-v1 (usable)
ChallengeUnavailable bool // announced challenge, unusable
HasRetryAfter bool
RetryAfterSec int
RawBody []byte
}
Plain denial (application/json) and remediable denial
(application/problem+json with a challenge member and optional
Retry-After) are both parsed. allow_unresolved/challenge is not a
verdict: an operation that comes back with outstanding obligations must not be
treated as allowed.
The evidence machine
Config.EvidenceProvider func(ctx, ref) (ok bool, err error) is the client
half of the evidence-facts mechanism. On a usable challenge the SDK:
1. rejects the retry when the challenge is stale (Challenge.Usable),
2. waits out Retry-After / retry_timing.not_before, never past expires_at,
3. asks the provider,
4. retries the byte-identical request once.
A second denial, stale/unparseable challenge, declined provider or provider error all reach the caller as-is. The SDK never transmits evidence itself — there is no wire channel for it.
DoOptions{Token, Audience, WithoutEvidenceRetry} tunes per-request behaviour.
Config (full field list)
See reference.md for the complete table with defaults; the short version by group:
- Mode/transport —
Mode(Bearer/MTLS),Key,Token,CertFile,KeyFile,ServerCA,Transport - Local minting —
Issuer,Audience,Subject,Realm,ID,Capabilities,DelegationMode - Remote issuance —
RemoteIssuer(URL),DA,UserSigner,DASignerMode("local"/"signer"),DASigner,IssuerClientID/Secret - TLS for SDK-managed outbound —
RootCA(signer/issuer/LLM trust bundle) - Ops —
Logger,LogFile - Evidence —
EvidenceProvider - LLM —
LLMConfig
identity subpackage
AgentKey (ES256/EdDSA): SPKI, Thumb (RFC 7638), Principal(), Cnf(),
Marshal/Write/ParsePEM. GenerateKey(t), Load(path, t).
bearer subpackage
Sign(key, opt), SignDA(opt) (signed with the principal key; v2 DA binds the
agent SPKI by default), SignOuter, IssuerClient.Exchange (RFC 7523; rejects
non-HTTPS endpoints by default).
transport subpackage
BearerTransport (header injection, refresh provider), NewMTLSTransport
(mTLS RoundTripper), TransportWithCA, PoolFromFiles, MTLSConfig.Load.
BearerTransport.RoundTrip is -race clean on SetToken; the refresh path
resets bodies via GetBody and only retries idempotent or replayable requests.