Execution trust model

Official signature trust, permission enforcement, executor capabilities, and immutable remote execution.

Adversary execution trust model

Purpose

An adversary is executable code. The CLI must preserve a fast local workflow without treating every remote package as equally trustworthy. Official signature verification, manifest permissions, executor selection, executor capabilities, and the final policy decision are separate inputs. None of them substitutes for another.

This document specifies:

  • which installed packages may use host execution;
  • how official catalog signatures are verified;
  • how manifests request portable isolation boundaries;
  • how the CLI verifies that the selected executor can enforce mandatory boundaries;
  • how mutable remote references are pinned to a digest; and
  • how a user's teams can become trusted without trusting spoofable names (proposed).

For envelope format, key ids, and signing operations, see Official signatures.

Security boundary at a glance

Rendering diagram…

The policy decision is made before the child process starts. A digest proves which bytes are selected; a valid official signature over that digest proves Doomer endorsed those bytes for host execution. Neither is code review or malware analysis.

Terms

TermMeaning
Local sourceA project selected by an explicit filesystem path, such as . or ../my-adversary, and not materialized from the artifact store.
Official signatureEd25519 signature over the artifact content digest, attached as an OCI referrer and verified with a public key baked into the CLI.
Official-signedAn installed package whose stored signature envelope verifies for its digest with this CLI binary’s keyring.
UntrustedAny installed remote package without a valid official or hosted namespace signature. Untrusted does not mean malicious; it means host execution is not implicitly authorized.
Requested boundaryIsolation described by the manifest or CLI. Organization policy may reject the request.
Mandatory boundaryA requested boundary that must be enforced by the selected executor.
ExecutorThe backend that launches adversary code and truthfully reports its enforceable capabilities.

Current trust policy

The production policy (OfficialSignatureTrustPolicy) has exactly two automatic trust cases:

  1. Explicit local source is trusted as local-source (the developer chose the path).
  2. Installed packages with a verified official signature are trusted as trusted-publisher for host execution.

Everything else is untrusted (unknown-publisher in the policy enum). In particular:

  • path allowlists and registry hostnames alone do not grant trust;
  • adversarylabs/* or a domain/name catalog id is not trusted unless the signature verifies;
  • a remote artifact does not become local source merely because it has been pulled and materialized on disk; and
  • a user's team namespace is not yet trusted automatically (see proposed team trust below).
Rendering diagram…

Released CLI binaries embed only the production public key (official-prod). Dev builds embed only the dev key (official-dev). See Official signatures.

Trust and backend decision table

Source/trustHostExecutorSandbox executorUser-facing behavior
Explicit local sourceAllowedAllowed when availableNo trust warning. Host is the default.
Official-signed installed packageAllowedAllowed when availableReport publisher identity, resolved digest, and backend. No alarm-style warning.
Untrusted installed packageBlocked by defaultAllowed when the executor satisfies policyNon-interactive: fail with guidance to use --allow-unsafe-host-execution or a sandbox. Interactive TTY: prompt Run anyway? [y/N].
Untrusted with --allow-unsafe-host-execution or TTY yesAllowedNot requiredPrint an explicit untrusted-host warning naming the adversary and digest.

The unsafe override changes only the signature-trust decision. It does not add isolation, grant permissions forbidden by organization policy, or let the CLI ignore mandatory manifest or CLI requirements.

Untrusted host UX

When host execution would run an untrusted package:

  1. If stdin and stderr are TTYs, the CLI prompts before launch and accepts only y / yes (default is no). Ctrl+C cancels the prompt.
  2. Otherwise the run fails unless --allow-unsafe-host-execution is set.
  3. If the override (flag or confirm) is accepted, stderr prints a clear untrusted warning; the process still runs with full user privileges.

Executor model

go
type Executor interface {
    Run(context.Context, RuntimeSpec) (RuntimeResult, error)
    Backend() ExecutorBackend
    Capabilities() ExecutorCapabilities
}

type ExecutorCapabilities struct {
    FilesystemReadIsolation  bool
    FilesystemWriteIsolation bool
    EnvironmentIsolation     bool
    NetworkIsolation         bool
    CPULimits                bool
    MemoryLimits             bool
    ProcessLimits            bool
}

HostExecutor

HostExecutor is a first-class backend and is the correct default for trusted local development and official-signed catalog packages. It launches a normal process with the user's operating-system authority. It currently reports every isolation capability as false.

Consequently, host-executed code may access resources available to the user, including files, environment variables, credentials, processes, and the network. Signature trust authorizes this risk; it does not reduce it.

NativeSandboxExecutor

This backend identity is reserved for native OS isolation. Linux Landlock is the first implementation target. The backend must report only capabilities it actually enforces for the current platform and invocation.

macOS sandboxing is a separate future backend effort. It is not a prerequisite for local HostExecutor use.

Manifest permissions

The canonical manifest syntax is:

yaml
permissions:
  enforcement: advisory # advisory is the default; required is fail-closed
  filesystem:
    read:
      - src
    write:
      - .adversary-output
  network: false
  environment:
    allow:
      - PATH
      - HOME

The old permissions.env spelling is not supported.

Manifest fieldRequested capability
Non-empty filesystem.readFilesystemReadIsolation
Non-empty filesystem.writeFilesystemWriteIsolation
network: falseNetworkIsolation
Non-empty environment.allowEnvironmentIsolation

Empty lists request no boundary. enforcement: advisory records the portable isolation request but does not require the current executor to enforce it. enforcement: required makes every requested manifest boundary mandatory.

--no-network always requests mandatory network isolation, regardless of the manifest's enforcement mode. Because HostExecutor reports NetworkIsolation: false, --no-network fails before host launch.

Permission evaluation

Rendering diagram…

The checks answer different questions:

  1. Requested: What isolation does the manifest or caller describe?
  2. Allowed: Does organization policy permit that request for this trust identity?
  3. Required: Which requested boundaries are mandatory for this run?
  4. Supported: Can the selected executor enforce every mandatory boundary?
  5. Trusted: May this package use the selected backend, especially host execution? (Local path selection or verified official signature.)

No unsupported mandatory boundary is silently ignored.

Immutable remote execution

Mutable references are resolved exactly once per run:

Rendering diagram…

The CLI never resolves the tag again during that execution. For remote runs it reports publisher identity, immutable digest, and backend on stderr so structured review output on stdout remains valid.

For an untrusted package using the unsafe override or TTY confirm, the CLI additionally emits a prominent untrusted warning.

Pull and offline verification

On doomer pull, the CLI best-effort fetches signature referrers. Official catalog signatures verify with the binary's keyring. Hosted private packages also fetch the authenticated platform trust root and verify the team delegation, registry, exact repository, and digest. Verified evidence is stored locally so later doomer run can trust host execution offline.

If no valid signature is available at pull or run time, the package remains untrusted for host execution.

Authenticated team trust

Automatically trusting “my team” is desirable only when the relationship is authenticated. A namespace string, email domain, team display name, local configuration value, or successful registry login is insufficient evidence.

Team-published packages are automatically signed only in the team's private namespace on registry.doomer.ai. The signing key stays server-side.

Server delegation

The signing service returns a root-endorsed public team key and a digest signature bound to a concrete source, for example:

json
{
  "registry": "registry.doomer.ai",
  "repository": "acme-security/private-review",
  "namespace": "acme-security",
  "teamId": "team_01...",
  "subjectDigest": "sha256:...",
  "keyId": "namespace:team_01...:v1"
}

The API must derive this mapping from authenticated team membership and registry ownership records. The client must not construct it from a display name or assume that a team slug owns an identically named namespace.

Trust rule

A remote publisher becomes trusted for the current profile when all of the following are true (in addition to any official-signature path above):

  1. The pull uses an authenticated Doomer profile authorized for the private repository.
  2. The platform root verifies the team key delegation.
  3. The delegated team key verifies the artifact signature.
  4. The signed registry, exact repository, and digest all match the pulled artifact.

Any mismatch or unavailable assertion produces untrusted, not a best-effort trust result.

Failure and lifecycle rules

  • A first pull must be online and authenticated. Previously verified evidence can be used for offline runs of the same immutable digest.
  • Malformed or unverifiable delegations and signatures fail to untrusted.
  • A different registry host or repository does not match cached evidence.
  • Logout prevents new private pulls and trust-root refreshes; it does not erase already verified evidence for immutable artifacts.
  • Team removal prevents future pulls and signatures but does not rewrite the provenance of an artifact authorized while membership was valid. Emergency key revocation and online freshness checks are required follow-up work before treating this as a high-assurance code-review approval system.
  • Team trust authorizes host execution but never overrides mandatory permission enforcement.
  • Enterprise trust stores may add or remove trusted identities independently; their precedence and audit logging must be explicit.

Examples

Local source

sh
doomer run . --path ../project
  • Trust: local-source
  • Default backend: HostExecutor
  • Warning: none
  • Digest display: not applicable to source selected directly from disk

Official-signed catalog package

sh
doomer pull go/security
doomer run go/security --path ../project
  • Trust: verified official signature for the resolved digest
  • Default backend: HostExecutor
  • Output: identity, immutable digest, backend

Official free-catalog packages (domain/name ids such as go/security, ci/github-actions) are signed at release with official-prod. A current release CLI verifies those signatures automatically.

Untrusted remote package

sh
doomer run ghcr.io/acme/private-review:1.0.0 --path ../project

Without a valid official signature, host execution is blocked unless a capable sandbox is selected, the caller confirms on a TTY, or they pass:

sh
doomer run ghcr.io/acme/private-review:1.0.0 \
  --path ../project \
  --allow-unsafe-host-execution

Mandatory network isolation on host

sh
doomer run . --path ../project --no-network

This fails before launch because HostExecutor cannot enforce network isolation. The fact that local source is trusted does not change executor capabilities.

Non-goals

This model does not claim that:

  • signature verification is code review or malware analysis;
  • a digest alone proves publisher authenticity (the signature over the digest does);
  • HostExecutor provides isolation;
  • a future sandbox provides capabilities it does not explicitly report;
  • complete macOS App Sandbox, Windows AppContainer, or container execution is currently implemented; or
  • manifest permissions and signature trust are interchangeable.

Implementation invariants

Changes to the CLI, API, registry, or trust store must preserve these rules:

  1. Explicit local source continues to work with HostExecutor without friction.
  2. Untrusted remote packages never receive silent host execution.
  3. The unsafe override and TTY confirm always produce an explicit warning.
  4. Mandatory permissions fail closed when unsupported.
  5. Executors report guarantees truthfully and per platform.
  6. Remote mutable references resolve once and execution uses that digest.
  7. Remote execution reports identity, digest, and backend.
  8. Remote trust comes only from verifying an official signature, a registry-and-repository-bound namespace signature, or an explicit enterprise trust-store decision—never from a path or hostname match alone.