Verified remote issuer client¶
Historical record
This page records a Celln development milestone (September 2026). Interfaces and limits it describes may since have changed or been replaced by the Celln fleet. For current behaviour see Celln Backend and Celln Fleet Installation.
cellnreview.NewIssuerClient is the controller-side client for the
host issuer service. It is also available through the
operator celln-tool issue-remote AGENT command. It is not yet called by the
AgentRun reconciler and does not dispatch an execution.
The client takes operator configuration only: an HTTPS origin, absolute controller bearer-token file, and an optional absolute CA bundle. Omitting the CA bundle uses system trust; supplying one restricts trust to that bundle. TLS 1.3, certificate chain and hostname verification are mandatory. URL credentials, path prefixes, queries, fragments and plaintext origins refuse. Ambient HTTP proxies are not used, redirects are not followed, and there is no skip-verification option. Recreate the client to reload a changed CA bundle. Token contents are reread for each request so mounted-file rotation does not require recreation.
Identity and retry contract¶
Issue(ctx, loader, frozen, approval, artifacts) builds the expected execution
candidate independently from the caller's live, uncached API reader and trusted
authority sources. It sends one provisioning POST, with no application-level
retry. Requests and responses are bounded to 1 MiB; requests embedded in returned
issuance records are bounded to 64 KiB. The response must explicitly say that
execution was not performed and readiness was not checked.
The complete operation has a 100-second context ceiling (or the caller's earlier
deadline), including API revalidation; the API reader must honor cancellation.
Before returning, the client checks the candidate's approval, profile identity,
grant hash and complete returned execution request, then revalidates live approval
again. Only the grant self-reference and three known Rust serialization defaults
are normalized: absent forge to null, absent inputs to an empty array and
absent invocation args to an empty array. Unknown fields and any changed task,
persona, caller, execution ID, tools, schemas, artifact or resource ceiling
refuse. JSON numbers retain their exact representation; they are not rounded
through floating-point conversion.
Transport/read failures return ErrIssuerOutcomeUnknown. A lost response can
mean the host has durably issued a profile. Preserve the original frozen request,
approval, artifact identities and candidate before the call; retry only those
same values. Other refusal/validation errors also do not authorize changing
identity or bypassing host replay rules. The client neither renews expiry nor
creates another run/attempt, withdraws local files, executes a task or claims
that a serving node is warm.
Operator command¶
issue-remote uses the existing independent grant-source, --run,
--model-policy, explicit --tool NAME@REVISION, --execution-mote and
--execution-closure flags, plus:
--issuer-url https://issuer.example.internal:8788 \
--issuer-token-file /etc/sympozium/controller-issuer-token \
--issuer-ca-file /etc/sympozium/issuer-ca.pem
There are no local policy-root, Celln binary, signing-key, composer or lifetime
flags: those remain host service configuration. The output is labelled
sympozium.ai/celln-remote-issuance-report-v1, not a local withdrawal report.
Loss of output does not trigger local filesystem withdrawal; the remote durable
window and periodic host reconciliation remain in force.
This command is an explicit operator provisioning invocation and resolves the
current plan each time. Do not blindly rerun it after an ambiguous outcome if
approval or run state may have changed. Durable controller retry must instead
reuse its saved frozen/approval/artifacts through the client API. The
IssueForRun helper below persists that state; wiring the AgentRun reconciler
remains the next integration gate.
Durable AgentRun provisioning¶
IssueForRun(ctx, writer, uncachedReader, runKey, loader, seed) saves
status.cellnIssuance before contacting the host. The first call requires an
IssuerRequest seed. A retry may pass nil to resume the saved request; supplying
a changed seed refuses. The operation has a 110-second ceiling.
The saved Prepared record pins the operator issuer endpoint, frozen selection,
model approval, artifacts, independently derived candidate and payload SHA-256.
After verified issuance, a status update commits Issued and the returned result.
The CRD enforces immutable payload/target/hash/result and a monotonic phase,
including refusal to remove issuance or the entire status. These schema rules
protect transitions, not payload authenticity: the helper independently derives
and validates the candidate and live run/approval again on resume.
A failed preparation write makes no remote call. A lost preparation acknowledgement resumes the stored payload. A lost remote outcome or failed result commit retries the identical issuance, subject to host journal/expiry rules. A lost acknowledgement after result commit resumes the validated stored outcome without another issuance POST. API conflicts never authorize replacing a concurrent plan. A changed run identity/spec, terminal/deleting run, existing dispatch identity, changed target or changed approval refuses. No retry extends the host profile expiry.
The endpoint string is not a cryptographic host-instance identity. Operators must use a stable per-host issuer endpoint; DNS/load-balancer failover and fleet ownership are not established by this helper. A stored result is not a readiness or continuing authorization claim: serving-side checks and expiry still apply. Status contains task/policy metadata, but no provider credential contents; namespace read permissions must reflect that sensitivity.
This helper does not set cellnRequest, cellnActionID, run phase or readiness.
Until the catalogue dispatch bridge is connected, the legacy reconciler explicitly
refuses any run with issuance state instead of silently forging its task.
Failure-injection tests cover both sides of each status commit, immutable retry
identity and the no-legacy-dispatch guard. The opt-in
test/integration/test-celln-issuance-status.sh checks transitions against the
isolated Kind API server without Jobs or model calls. The separate KVM composition
test exercises durable preparation, actual TLS/host issuance, saved-result resume
and managed approval withdrawal with fake Kubernetes metadata. Neither test proves
the final deployed catalogue-backed execution journey.
Hand-off to the dispatch journal¶
FreezeIssuedDispatch(ctx, writer, uncachedReader, runKey, loader) validates a
committed issuance against current approval and saves its exact request bytes and
catalogue-derived execution ID in status.cellnRequest/status.cellnActionId.
It requires a pending, live Celln run and has a 15-second ceiling. Prepared-only
issuance refuses. Conflicting or partially populated dispatch state refuses;
identical retries reuse the saved outcome without contacting the issuer.
A failed or ambiguous status commit returns no dispatch bytes. Retrying after a lost commit acknowledgement verifies and returns the identical saved request. The helper does not set Running/StartedAt, choose a route, prewarm a process or submit an execution. The legacy dispatch guard remains in force until a dedicated catalogue bridge consumes this journal. Host admission and expiry must still be checked on submission; a successful hand-off is not a readiness or live grant guarantee. In particular, do not replace the catalogue-derived ID with the legacy name/UID ID, or remarshal the request through a different wire representation.
Frozen serving route¶
IssuerClientOptions.Route optionally takes operator-selected routerURL and
backend origins. The router URL must be HTTPS; the backend must be an exact HTTP
origin from that router's configured list, matching the router's current backend
transport contract. No paths (including trailing slash), URL credentials, query,
fragment, whitespace or invalid ports are accepted. The constructor copies this
configuration so changing the input object cannot retarget the client.
The route is included in the existing immutable, hashed Prepared payload before
provisioning. Both IssueForRun and FreezeIssuedDispatch compare it to current
operator configuration. A changed router/backend, adding a route to historical
unrouted issuance, or removing an existing route refuses. These are identity
changes, not retry configuration. Existing nil-route provisioning callers remain
compatible; that compatibility does not authorize routed submission.
This freezes names, not network identity: it does not verify that the issuer and
serving endpoint are the same host, qualify DNS/load-balancer failover, contact
the router, or claim that artifacts are warm. The forthcoming controller bridge
must require a configured route, use a separately scoped router credential,
verify prewarm responses and send X-Celln-Backend only from this frozen operator
configuration. Ordinary tenant task/spec data must never supply that header.
Evidence¶
Tests cover verified TLS, token rotation, untrusted certificates, changed responses, malformed/oversized results, redirects, a lost response, post-response approval withdrawal and refusal of unsafe configuration. The explicit real-KVM test uses this client against the real TLS issuer service and actual Celln compositor/member verifier; identical retry keeps profile/grant identity and periodic approval withdrawal refuses further issuance. Kubernetes is a fake client, no model calls are made, and this is not a deployed controller proof.