Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00
..

Device Authority Go Client

device-authority-go is the narrow, shared Device Authority owner-lifecycle client boundary for Tutti and TSH. It prevents the security-sensitive enrollment and owner-token signing protocol from drifting between products.

This initial extraction is a staged cross-repository bootstrap from TSH's production implementation. The stable module is published first; TSH then switches to the released module without behavior changes; Tutti consumes the same boundary next. Cross-repository consumers must not bridge that ordering with a workspace replacement or pseudo-version. Update this section when those consumer cutovers land so it continues to describe current integration state.

The module owns:

  • Device Authority ensure, gateway identity enrollment, owner-tunnel token, and lease-renewal HTTP wire contracts;
  • the canonical tsh.gateway.owner-session.v1 Ed25519 signing payload;
  • response-to-request credential binding checks;
  • ErrResponseBinding for adapters to fail closed when a lease response is bound to a different authority;
  • bounded response and HTTP error metadata handling;
  • a reusable process-local Ed25519 identity source compatible with TSH's current behavior.

The module does not own account cookies, bearer tokens, user/device/lane headers, endpoint selection, identity persistence policy, Relay demand, reconnection, lease scheduling, logging, or product retry decisions. Consumers inject those concerns through Config and IdentitySource. Relay byte-stream mechanics remain in packages/device-link/relaytransport.

Usage

identities := deviceauthority.NewMemoryIdentitySource()
client, err := deviceauthority.NewClient(deviceauthority.Config{
    BaseURL:   controlPlaneBaseURL,
    APIPrefix: controlPlaneAPIPrefix,
    HTTPClient: productHTTPClient,
    Identities: identities,
    PrepareRequest: func(req *http.Request, metadata deviceauthority.RequestMetadata) error {
        addCurrentProductHeaders(req, metadata.OwnerUserID)
        return nil
    },
})

APIPrefix is required because choosing /v1 versus a product gateway path is deployment policy. The shared client never guesses from the host name.

MemoryIdentitySource retains one key per runtime only for the current process. It is intended for consumers preserving an existing process-local identity contract. A product that requires restart-stable identity must implement IdentitySource with its durable credential store and must return the same signing identity for enrollment retries and token issuance.

The owner sequence is:

product demand
  -> EnsureDeviceAuthority
  -> RegisterDeviceGatewayIdentity
  -> IssueDeviceGatewayOwnerTunnelToken
  -> product starts relaytransport.OwnerHost
  -> RenewDeviceAuthorityLease on the product schedule while its server
     `ExpiresAt` remains fresh
  -> product releases Relay demand

Do not log private keys, tokens, enrollment proofs, signatures, raw response bodies, or request headers. HTTPError.Body exists for adapter-owned protocol handling and is bounded to 4096 bytes; ordinary logs should use status and categorical reasons instead. The ordinary error string contains only the HTTP status and never includes Body.

Validation and releases

Run:

go test -race ./...

The module participates in Tutti's shared stable packages/** Go release cohort. Cross-repository consumers must use the released module tag and must not add a workspace replacement or pseudo-version.