Skip to content

symfonic.agent.receipt_configuration

receipt_configuration

Opt-in host attestation, never independent proof of opaque executable truth.

The authenticated host must accurately declare immutable deployment/binding, artifact identities, and tool/output schemas. Equality proves only equality to that declaration. Hidden state, closures and remote deployments remain the host's responsibility. No provider default, binding or schema hook is invoked.

HostReceiptConfiguration dataclass

HostReceiptConfiguration(canonical_json: str)

Canonical immutable JSON attested by the authenticated host authority.

Required keys: provider_deployment (nonempty immutable deployment identity), provider_binding (object naming all binding settings), executable_artifacts (exact executable-name -> immutable artifact identity), output_schema (object or null), tool_schemas (exact tool-name -> schema object). Secrets belong in host-owned opaque binding identities, never in this declaration.

configuration_fingerprint

configuration_fingerprint(agent: Any, authority: Any, versions: Any, output_type: Any) -> tuple[str, ModelConfig]

Read explicit values and static Python functions; never execute hooks.

Source code in src/symfonic/agent/receipt_configuration.py
def configuration_fingerprint(agent: Any, authority: Any, versions: Any,
                              output_type: Any) -> tuple[str, ModelConfig]:
    """Read explicit values and static Python functions; never execute hooks."""
    declaration = getattr(authority, "receipt_configuration", None)
    if type(declaration) is not HostReceiptConfiguration:
        _refuse()
    manifest = declaration.document()
    plans = agent._plans
    if type(plans.model) is not ModelConfig or plans.role_models:
        _refuse()  # A callable/default resolver cannot be fingerprinted safely.
    tools = plans.tools.tools
    names = {tool.name for tool in tools}
    expected = {"provider"} | {f"tool:{name}" for name in names}
    if (set(manifest["executable_artifacts"]) != expected
            or {name for name, _ in versions} != expected
            or set(manifest["tool_schemas"]) != names
            or (manifest["output_schema"] is None) != (output_type is None)):
        _refuse()
    provider_code = inspect.getattr_static(plans.model_provider, "get_chat_model", None)
    executable_code = {"provider": _code(provider_code)}
    for tool in tools:
        executable_code[f"tool:{tool.name}"] = [
            _code(inspect.getattr_static(tool, name, None))
            for name in ("func", "coroutine", "_run", "_arun")
        ]
    # Exact dataclass fields only: opaque http_client or nested values refuse.
    model = _canonical({field.name: getattr(plans.model, field.name)
                        for field in fields(ModelConfig)})
    material = {"host_attestation": manifest, "model": model,
                "instructions": plans.instructions, "budget": plans.max_model_rounds,
                "executables": versions, "local_code": executable_code,
                "output_identity": None if output_type is None else
                    [output_type.__module__, output_type.__qualname__],
                "effects": sorted(plans.authorized_effects),
                "capabilities": list(plans.capability_names)}
    # JSON serialisation above accepts lists, not tuples; versions are values.
    material["executables"] = [list(item) for item in versions]
    digest = hashlib.sha256(_json(material).encode("ascii")).hexdigest()
    return digest, ModelConfig(**model)