# K1 HTTP orchestration
This library provides one listener-free Axum spine. Its complete public API is:
```rust
pub use kcode_k1_http_signature::CanonicalUsername;
pub struct Config {
pub server_id: String,
pub public_origin: String,
pub max_body_bytes: usize,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Identity;
impl Identity {
pub fn new(user_id: [u8; 12], public_key: [u8; 32]) -> Self;
pub fn user_id(&self) -> &[u8; 12];
pub fn public_key(&self) -> &[u8; 32];
}
#[derive(Clone)]
pub struct Principal;
impl Principal {
pub fn user_id(&self) -> &[u8; 12];
pub fn username(&self) -> &CanonicalUsername;
}
#[derive(Clone)]
pub struct RegistrationPrincipal;
impl RegistrationPrincipal {
pub fn username(&self) -> &CanonicalUsername;
pub fn public_key(&self) -> &[u8; 32];
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IdentityError { Unavailable }
#[async_trait]
pub trait IdentityProvider: Send + Sync + 'static {
async fn lookup(&self, username: &CanonicalUsername)
-> Result<Option<Identity>, IdentityError>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ConfigError {
EmptyServerId,
InvalidPublicOrigin,
InvalidBodyLimit,
}
pub struct K1Http;
impl K1Http {
pub fn new(
config: Config,
replay: kcode_k1_http_replay::ReplayWindow,
identities: Arc<dyn IdentityProvider>,
) -> Result<Self, ConfigError>;
pub fn router(
&self,
registration: axum::routing::MethodRouter,
authenticated: axum::Router,
) -> axum::Router;
}
```
`IdentityError` and `ConfigError` implement `Display` and `Error`. `server_id` must be nonempty, `max_body_bytes` nonzero, and `public_origin` an absolute HTTP(S) origin with authority, no userinfo, query, fragment, or non-root path. Its exact text is signed. Its exact authority is the sole accepted `Host`; forwarding headers are ignored.
`router` mounts the supplied POST registration handler at `/api/register` and nests supplied authenticated routes below `/api`. A registration handler extracts `RegistrationPrincipal`; authenticated handlers extract trusted `Principal`. A collision at `/api/register` may fail router construction. Unknown routes and method mismatches have no package-owned effect.
Every protected request has exactly one `K1-Username`, `K1-Epoch`, `K1-Nonce`, `K1-Body-SHA256`, and `K1-Signature`; registration also has one `K1-Public-Key`. Binary fields use canonical unpadded Base64URL and fixed 16-, 32-, 64-, and 32-byte decoded lengths. Epochs are decimal `u64`. Content-Type is absent or unique and its exact ASCII value is signed. Method and the original encoded origin-form path and query are signed without query or percent normalization.
Authentication canonicalizes the username, validates the epoch, performs one identity lookup, verifies K1-HTTP-1 against the claimed digest, collects and hashes the bounded body once, then admits the nonce and restores the body. The private replay identity is the 12-byte user ID zero-extended to 32 bytes. Invalid signatures consume no nonce; successful authentication consumes it before the endpoint runs. Unknown identity and failed verification both return `authentication_failed`. Registration instead verifies the candidate key and body, attaches `RegistrationPrincipal`, and performs neither lookup nor replay admission. Durable registration replay and idempotency belong to the supplied registration handler.
Authority validation precedes routes and CORS preflights. CORS allows any origin without credentials, GET, POST, PUT, PATCH, DELETE, OPTIONS, and HEAD, and Content-Type plus every K1 request header; it exposes `K1-Epoch`. API responses, preflights, and routing failures carry `Cache-Control: no-store`, `X-Content-Type-Options: nosniff`, and the current `K1-Epoch` when available.
Errors are concise JSON: 421 `invalid_request_authority`; 400 `malformed_authentication_envelope`; 401 `stale_epoch`; 401 `authentication_failed`; 400 `body_digest_mismatch`; 413 `body_too_large`; 409 `replay`; 429 `nonce_capacity`; 503 `identity_provider_unavailable`; and 503 `epoch_unavailable`. No request, identity, key, nonce, path, body, provider, operating-system, or invite detail is emitted or logged.
Package work is O(header + body): one bounded body allocation and SHA-256 hash, one leaf Ed25519 verification, one identity lookup, and expected-O(1) replay calls. No HTTP-owned lock spans lookup or body collection, and a blocked lookup does not block unrelated requests. Replay synchronization and durable transitions remain replay-owned. Memory for a request is bounded by the configured body limit plus headers and verification state. There are no package-owned listeners, network/provider calls, retries, background tasks, sessions, cookies, or business authorization. The provider-free conformance suite exercises bodies through its configured 64 KiB boundary within the testkit's broad 30-second verifier envelope. Larger caller-configured valid bodies have the same linear work but no measured wall-clock promise.