Python: Web Bot Auth profile
Application profiles are not part of the core DNSid protocol — they are optional application-layer integrations built on top of it.
WebBotAuthProfile
Section titled “WebBotAuthProfile”from dnsid import WebBotAuthProfileWeb bot authentication helpers using DNSid-issued JWTs.
Implements the Agent Identity Assertion (AIA) pattern for HTTP bot auth:
- Signs outbound requests with
Authorization: Bearer <dnsid-jwt> - Verifies inbound requests by validating the Bearer token
An application profile layered on top of DNSid identity verification. Application profiles are not part of the core DNSid protocol and are not required for DNSid conformance.
WebBotAuthProfile constructor
Section titled “WebBotAuthProfile constructor”WebBotAuthProfile(resolver: IdentityResolver, key_provider: KeyProvider | None, domain: str, bot: BotIdentity | None = None, config: BotAuthConfig | None = None) -> NoneInitialize the profile for a local bot identity.
Arguments:
resolver(IdentityResolver): Any object satisfying the IdentityResolver protocol.key_provider(KeyProvider | None): Key management implementation for the local bot identity, or None for a verification-only profile.domain(str): FQDN of the local bot identity, or "" when verification-only.bot(BotIdentity | None): Bot metadata to include in signed JWTs. — defaultNoneconfig(BotAuthConfig | None): Optional freshness/verification overrides. — defaultNone
from_identity_manager
Section titled “from_identity_manager”WebBotAuthProfile.from_identity_manager(manager: object, bot: BotIdentity | None = None, config: BotAuthConfig | None = None) -> WebBotAuthProfileConstruct from a fully-initialised IdentityManager.
Reads domain and key_provider from the manager.
create_bot_token
Section titled “create_bot_token”WebBotAuthProfile.create_bot_token(target_url: str, *, expiry: datetime.timedelta | None = None, additional_claims: dict[str, Any] | None = None) -> strCreate a signed JWT for bot authentication.
The JWT includes:
iss/sub: the bot’s DNSid domainaud: the target URL origin (scheme + host)bot: bot identity metadataiat,exp,jti: standard temporal claims
Arguments:
target_url(str): The URL being requested. The audience is derived from its origin.expiry(datetime.timedelta | None): Token lifetime. Default 5 minutes. — defaultNoneadditional_claims(dict[str, Any] | None): Extra claims to include in the JWT payload. — defaultNone
Returns:
str— The signed JWT in compact serialization.
Raises:
ArgumentError: If the profile is verification-only,target_urlis not a valid URL, the requested expiry exceeds the configured maximum lifetime, oradditional_claimswould override a reserved claim.
sign_bot_request
Section titled “sign_bot_request”WebBotAuthProfile.sign_bot_request(request: httpx.Request, *, expiry: datetime.timedelta | None = None, additional_claims: dict[str, Any] | None = None) -> httpx.RequestSign an outbound httpx.Request with a bot auth JWT.
Adds an Authorization: Bearer <jwt> header to the request and
sets a User-Agent header incorporating the bot identity.
Arguments:
request(httpx.Request): The httpx.Request to sign.expiry(datetime.timedelta | None): Token lifetime override. — defaultNoneadditional_claims(dict[str, Any] | None): Extra claims for the JWT. — defaultNone
Returns:
httpx.Request— A new httpx.Request with the Authorization header set.
Raises:
ArgumentError: Propagated from token creation (invalid URL, expiry over the maximum, or reserved-claim override).
create_signed_bot_client
Section titled “create_signed_bot_client”WebBotAuthProfile.create_signed_bot_client(*, expiry: datetime.timedelta | None = None, additional_claims: dict[str, Any] | None = None, base_headers: dict[str, str] | None = None) -> httpx.ClientReturn an httpx.Client that automatically signs every request with bot auth.
Every outbound request gets an Authorization: Bearer <dnsid-jwt> header.
Arguments:
expiry(datetime.timedelta | None): Token lifetime override applied to every request. — defaultNoneadditional_claims(dict[str, Any] | None): Extra claims added to every JWT. — defaultNonebase_headers(dict[str, str] | None): Default headers applied to every request. — defaultNone
Returns:
httpx.Client— An httpx.Client that signs each request before sending it.
verify_bot_request
Section titled “verify_bot_request”WebBotAuthProfile.verify_bot_request(*, headers: dict[str, str], expected_audience: str, peer_cert: TLSCertificate | None = None) -> VerifiedBotRequestVerify an inbound bot request by validating its Authorization Bearer JWT.
Resolves the JWT issuer’s DNSid identity and verifies the signature.
Arguments:
headers(dict[str, str]): The HTTP request headers (case-insensitive lookup for Authorization).expected_audience(str): Required trusted origin of the server receiving the request; the JWTaudclaim must match this value.peer_cert(TLSCertificate | None): Certificate from the current peer connection. Required when the issuer’s DNSid record carriesfl=mtls. — defaultNone
Returns:
VerifiedBotRequest— A VerifiedBotRequest with the verified domain and bot metadata.
Raises:
VerificationError: If the token is missing, malformed, expired, or signature-invalid, or a claim fails validation; also propagated from issuer identity verification. Carries a VerificationCode.
BotAuthConfig
Section titled “BotAuthConfig”from dnsid import BotAuthConfigConfiguration for the Web Bot Auth profile.
Attributes:
max_lifetime(datetime.timedelta): Maximum acceptable JWT lifetime. Default 15 minutes.clock_skew(datetime.timedelta): Allowed clock skew tolerance. Default 60 seconds.require_bot_claim(bool): If True, verification rejects JWTs without abotclaim.
BotIdentity
Section titled “BotIdentity”from dnsid import BotIdentityMetadata describing the bot/agent for inclusion in the JWT bot claim.
Attributes:
name(str): Human-readable bot name (e.g. “AcmeSearchBot”).version(str): Bot version string (e.g. “1.2.3”).purpose(str): Short machine-readable purpose tag. Common values: “search-indexing”, “data-collection”, “monitoring”, “ai-training”, “summarization”.operator_url(str): URL with more info about the bot and its operator (like robots.txt contact info). Should be an HTTPS URL.
VerifiedBotRequest
Section titled “VerifiedBotRequest”from dnsid import VerifiedBotRequestResult of successful bot request verification.
Attributes:
domain(str): The verified DNSid domain of the bot.bot(BotIdentity | None): Bot identity metadata extracted from the JWTbotclaim, or None.verified_domain(VerifiedDomain): The full VerifiedDomain result from DNSid resolution.claims(dict[str, Any]): The decoded JWT claims.
WBAHttpSigner
Section titled “WBAHttpSigner”from dnsid import WBAHttpSignerSign outbound requests as Web Bot Auth HTTP Message Signatures.
Web Bot Auth is an application profile layered on top of DNSid identity; application profiles are not part of the core DNSid protocol and are not required for DNSid conformance.
WBAHttpSigner constructor
Section titled “WBAHttpSigner constructor”WBAHttpSigner(key_provider: KeyProvider, domain: str, *, config: WebBotAuthConfig | None = None, signature_agent: str | None = None, label: str = DEFAULT_LABEL, expires_in_seconds: int | None = None) -> NoneInitialize the signer for a local DNSid identity.
Arguments:
key_provider(KeyProvider): Provides the Ed25519 signing key. WBA signing requires Ed25519; other key types raise ArgumentError at signing time.domain(str): The DNSid FQDN, used to derive the default directory URL.config(WebBotAuthConfig | None): OptionalWebBotAuthConfig. — defaultNonesignature_agent(str | None): Back-compat override for the directory URL placed in Signature-Agent. — defaultNonelabel(str): Signature label and Signature-Agent dictionary member name (profile default:sig1). — defaultDEFAULT_LABELexpires_in_seconds(int | None): Back-compat override for the request signature TTL. — defaultNone
Raises:
ArgumentError: If the resolved Signature-Agent directory URL is not https.
WBAHttpSigner.sign(req: HttpRequest, variant: WBAVariant = WBAVariant.ACTIVE, options: WebBotAuthSigningOptions | None = None) -> HttpRequestSign req in place and return it.
REVOKED signs normally — revocation is a directory-state concern
checked by the verifier, not a property of the signature bytes.
Arguments:
req(HttpRequest): The request to sign; headers (and, forTAMPERED, the body) are modified in place.variant(WBAVariant): Signing variant to produce.NO_SIGreturns req unmodified; other variants produce deliberately broken signatures for verifier testing. — defaultWBAVariant.ACTIVEoptions(WebBotAuthSigningOptions | None): Optional per-request overrides. — defaultNone
Returns:
HttpRequest— The same request, with Signature-Input, Signature, and (when enabled) Signature-Agent headers set.
Raises:
ArgumentError: If the signing key is not Ed25519, or an additional component is not a known derived component or a lowercase HTTP field name.
WBAVariant
Section titled “WBAVariant”from dnsid import WBAVariantSigning variants the test driver toggles (§7).
Members:
ACTIVE='active'REVOKED='revoked'KEY_MISMATCH='key-mismatch'NO_SIG='no-sig'TAMPERED='tampered'EXPIRED='expired'
WBADirectoryResponse
Section titled “WBADirectoryResponse”from dnsid import WBADirectoryResponseA signed HTTP Message Signatures Directory response.
WebBotAuthConfig
Section titled “WebBotAuthConfig”from dnsid import WebBotAuthConfigWBA profile configuration (design doc 10 §Profile Configuration).
WebBotAuthSigningOptions
Section titled “WebBotAuthSigningOptions”from dnsid import WebBotAuthSigningOptionsPer-request overrides for CreateWebBotAuthSignedRequest.
serve_http_message_signatures_directory
Section titled “serve_http_message_signatures_directory”from dnsid import serve_http_message_signatures_directoryserve_http_message_signatures_directory(req: HttpRequest, key_provider: KeyProvider, config: WebBotAuthConfig | None = None) -> WBADirectoryResponseReturn the active public signing key as a signed WBA directory response.
The body is a JWKS object served with the
application/http-message-signatures-directory+json media type and one
HTTP Message Signature over "@authority";req, content-type,
cache-control, and content-digest with
tag="http-message-signatures-directory".
Arguments:
req(HttpRequest): The inbound directory request; its authority is covered by the response signature.key_provider(KeyProvider): Provides the Ed25519 signing key to publish and sign with.config(WebBotAuthConfig | None): OptionalWebBotAuthConfig; controls the directory signature TTL. — defaultNone
Returns:
WBADirectoryResponse— A WBADirectoryResponse with status, headers, and JWKS body.
Raises:
ArgumentError: If the signing key is not Ed25519.