Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System Architecture

AHRI TRE is a governance-aware, provenance-first research environment. A Deployment’s Trusted runtime owns live datastore capabilities, authentication, Secret resolution, governed workflows, and content admission. User-side clients and the Web control plane reach those capabilities through authenticated interfaces.

The datastore combines two stores:

  • PostgreSQL holds metadata, governance, access control, provenance, and domain records, as well as the DuckLake catalog.
  • DuckDB and DuckLake manage analytical data in configured Lake storage, with Arrow RecordBatch as the in-memory tabular boundary, Parquet for persisted datasets, and Arrow IPC for tabular transfer.

Cross-store work uses explicit admission and compensating cleanup. A PostgreSQL transaction cannot atomically commit Lake files and metadata together.

This chapter describes the implemented runtime and Client parity architecture. The parity completion audit records the qualified CLI/C and Browser artifacts. The final section separates the broader Governed-compute design from those delivered capabilities.

Architecture Views

User View

Researchers, custodians, and automation use authenticated Sessions and Study context. Datastore entry, Study access, custodianship, DUO restrictions, and Asset risk classification remain distinct concerns. Discovery metadata does not grant access to protected content.

flowchart LR
    user["Researcher / custodian / automation"]
    browser["TRE Browser<br/>Discovery and access requests"]
    client["CLI / language binding"]
    session["Authenticated Trusted Session<br/>Execution profile and Current study"]
    workflow["Governed application workflow"]
    catalog["Catalogue and provenance<br/>Studies, Domains, Assets, semantic model"]
    source["Source files / HTTPS / SQL / REDCap"]
    assets["Governed Assets<br/>Datafiles and Datasets"]
    disclosure["Authorized low-risk disclosure"]
    destination["Explicit user-controlled copy"]

    user --> browser
    user --> client --> session --> workflow
    browser --> catalog
    source -->|authorized acquisition and ingest| workflow
    workflow --> catalog
    workflow --> assets
    assets --> disclosure --> destination

System Integrator View

There are three separate entry paths: ordinary user Clients, the Browser’s Web control plane, and trusted operator administration. Their credentials and permissions are not interchangeable.

flowchart TB
    subgraph clients["User-controlled clients"]
        cli["CLI and automation"]
        daemon["ahri-tred<br/>local lifecycle and Managed forwarding"]
        wrappers["Python / Julia / R / other bindings"]
        cabi["Stable C ABI"]
        managed["Managed runtime<br/>Client Session context and HTTPS transport"]
        cli --> daemon --> managed
        cli -->|content and acquisition| managed
        wrappers --> cabi --> managed
    end

    browser["TRE Browser<br/>separate TypeScript application"]
    web["Web control plane<br/>OIDC, cookies, CSRF, HTTP projection"]
    idp["ORCID / OIDC provider"]
    operator["Trusted operator client"]

    subgraph trusted["Deployment-scoped Trusted runtime"]
        api["Authenticated interfaces<br/>user, Web service, local operator"]
        sessions["Runtime logins and live Sessions"]
        app["App-owned workflows<br/>governance, writers, disclosure"]
        adapters["Metadata, Lake and source adapters"]
        api --> sessions --> app --> adapters
        api -->|scoped service / operator capability| app
    end

    pg["PostgreSQL<br/>TRE metadata and DuckLake catalog"]
    lake["Lake storage<br/>Datafiles and Parquet"]
    sources["External sources"]

    browser --> web
    web <-->|browser authentication| idp
    web -->|mTLS service identity and scoped protocol| api
    managed -->|Runtime credential over HTTPS| api
    operator -->|protected local Unix socket| api
    sessions <-->|server-owned OAuth lifecycle| idp
    adapters --> pg
    adapters --> lake
    adapters --> sources

Web has no PostgreSQL or Lake credential, mount, direct adapter dependency, or deployed datastore route. Its readiness check authenticates to the Trusted runtime and checks capabilities. The user-side Managed runtime likewise has no TRE datastore or Lake authority. Client acquisition can access an external source using the user’s source credentials; that is separate from datastore execution. See ADR-0010 and ADR-0015.

Developer View: High-Level Layers

The four parity modules are behavioral boundaries within the existing crates. They concentrate shared decisions without requiring four new crates.

ModuleCurrent owner and responsibility
Client Sessionahri_tre_runtime::ClientSessionContext shares CLI/C selection, exact-reference binding, registry operations, profile preferences, and lifecycle-result interpretation. ahri_tre_session persists safe client context.
App-owned workflowahri_tre_app owns authorized selector resolution, semantic validation, governance, mutation sequencing, provenance, compensation, and safe results.
Trusted content-disclosureAn application workflow seam admits immutable inputs and authorization/risk snapshots; Trusted composition supplies live Session capabilities and delivers bounded content with durable evidence.
Parity qualificationShared scenarios exercise installed CLI and exported C functions against real TRE services, with matching Browser companion checks and explicit artifact, configuration, and authority scope. This is a verification boundary, not a production executor.

The decisions are ADRs 0020, 0021, 0022, and 0023.

flowchart TB
    cli["CLI / local daemon"]
    ffi["C ABI / language bindings"]
    client["ClientSessionContext<br/>Managed transport and safe context"]
    web["Web HTTP adapter<br/>service / visitor / custodian context"]
    protocol["ahri_tre_protocol<br/>versioned DTOs, references, errors"]
    runtime["Trusted runtime composition<br/>authenticate, retain capabilities, dispatch"]
    app["ahri_tre_app<br/>workflow decisions and disclosure admission"]
    domain["ahri_tre_types + ahri_tre_core<br/>records, policies, repository contracts"]
    metadata["ahri_tre_pgmeta / runtime_pgmeta"]
    lake["ahri_tre_lake / ahri_tre_tabular"]
    sources["ahri_tre_sqlmeta / ahri_tre_redcap"]

    cli --> client
    ffi --> client
    client -->|user protocol| runtime
    web -->|service protocol| runtime
    protocol -.-> client
    protocol -.-> web
    protocol -.-> runtime
    runtime --> app
    app --> domain
    app --> metadata
    app --> lake
    app --> sources

The arrows show responsibility flow. Configuration, Secret capabilities, and runtime composition supply dependencies; workflows do not reopen configuration documents or Secret stores. The protocol crate contains serializable contracts and compatibility rules, with no workflow execution or datastore connections.

Developer View: Protocol Layer Detail

Ordinary JSON control-plane operations use protocol 2.0.0. The active Browser contract is v2 and shares canonical object references. Unsupported protocol majors fail before request-body decoding. Protocol, C ABI, library package, and content-frame versions are independent compatibility dimensions.

An ordinary CLI Session workflow follows:

CLI command
  -> ProtocolRequestEnvelope
  -> local daemon socket
  -> shared ClientSessionContext / Managed transport
  -> authenticated Trusted-runtime HTTPS interface
  -> exact owner-scoped live Session
  -> AppService workflow
  -> safe protocol response

The daemon owns local process lifecycle and forwarding. Only daemon.version and daemon.doctor use local public-protocol dispatch; private shutdown and lifecycle frames remain adapter-local. Missing configuration, an unavailable Trusted runtime, and unsupported remote operations fail without local datastore execution. Automation uses the same trusted workflow authority, with explicit Session and Study scope where required.

The C ABI route follows:

host-language wrapper
  -> opaque C client / Session handle
  -> shared ClientSessionContext / Managed transport
  -> authenticated Trusted-runtime HTTPS interface
  -> AppService workflow
  -> result handle containing response JSON and optional content payload

CLI content/upload/acquisition adapters and C payload calls use separate bounded interfaces on the same Trusted HTTPS origin. They do not put binary content or source credentials into ordinary JSON envelopes. C handle ownership and CLI presentation remain adapter concerns. Full host-language packages live in separate repositories under ADR-0005.

Configuration, Secrets, and Authentication

ahri_tre_config owns versioned Application and Client document parsing, validation, selection, and Effective configuration projection. Application configuration belongs to trusted operators. A generated Client bootstrap contains only schema version, Deployment identity, the canonical Trusted HTTPS origin, and public CA trust. Execution profiles, datastore topology, policies, and Secret references remain server-side.

ahri_tre_secrets owns Secret material, immutable Injected snapshots, and the encrypted Managed-secret store. Configuration contains logical references and declared Injected versions, never resolved material.

flowchart LR
    application["Application configuration"]
    config["ahri_tre_config<br/>validation and projection"]
    effective["Effective configuration<br/>selected graph and fingerprint"]
    bootstrap["TrustedRuntimeBootstrap<br/>immutable retained state"]
    secrets["ahri_tre_secrets<br/>Injected snapshot / Managed store"]
    capabilities["Resolved runtime and operation capabilities"]
    clientdoc["Generated Client bootstrap<br/>Deployment, origin, public trust"]

    application --> config --> effective --> bootstrap
    config --> clientdoc
    secrets -->|verified startup snapshots| bootstrap
    bootstrap --> capabilities
    secrets -->|newly authorized Managed-secret resolution| capabilities

Trusted bootstrap verifies the complete Managed store and retains Effective configuration, the Injected-secret snapshot, and startup-required Managed-secret snapshots before exposing listeners or authentication capabilities. Configuration and Injected changes require restart. A newly authorized operation may resolve its permitted Managed reference again; existing capabilities keep the material they opened. Configuration fingerprints describe non-secret configuration, not Secret values or mutable active Managed versions.

Runtime login is deployment-wide and precedes profile-specific Session opening. The Trusted runtime retains reusable upstream OAuth/OIDC artifacts as encrypted Managed secrets. Clients receive only a short-lived opaque Runtime client credential. Rotation, reauthentication, expiry, and logout preserve login-owner binding and revocation; they do not transfer a live capability to another user. A client-visible credential or reference is never a PostgreSQL credential.

AuthorityPermitted role
Runtime client credentialAuthenticate one Runtime login; open and use its authorized Sessions.
Live SessionRetain the authenticated datastore capability, selected Execution profile, and Current study.
Web mTLS service identityInvoke the explicit Browser service allowlist; it cannot become a Runtime user.
Browser visitor identityIdentify the requester from protected server-side Web session state.
Custodian Runtime credential plus matching SessionEstablish the human datastore actor for custodial Browser actions, in addition to Web service authentication.
Protected local operator socketAdmit deployment administration independently of ordinary Runtime login.

Web receives its own Effective service graph and authentication capabilities; it does not receive the Trusted runtime’s datastore graph or server private key. The operator handler resolves privileged dependencies inside Trusted authority. There is no user-client fallback to administration or infrastructure repair.

Version 1 uses one active Trusted runtime per Deployment. Administration uses the fixed /run/ahri-tre/admin.sock Unix socket, protected by filesystem permissions and kernel peer identity; a Runtime client credential cannot authorize it. Durable Dataset coordination state is separate from disposable trusted scratch, so replacing scratch does not erase writer ownership or unresolved cleanup evidence.

Development tooling, Local TRE, and Integration fixtures have separate lifecycles and authority. Opening the development container starts tools only; the durable Local TRE deployment and disposable Integration services are started explicitly with separate networks, credentials, and storage. See the development environment guide.

Sessions and Public Identity

A live Session exists only while the Trusted runtime retains its capability. The exact client reference is {deployment_id, runtime_login_id, session_id}. A reused friendly name cannot redirect an old reference to a new Session. Current study belongs to that live Session and is resolved and authorized by the application; it is not an executable client-side default.

CLI invocations share safe registry state within one local user, Deployment, and Runtime login. Each constructed C client starts with an independent registry shared by its handles. List and close-all operate on the invoking registry, not every Session owned by the same human. Freeing a local C handle releases memory; explicit remote close ends the shared capability for all attached clients. A lost response never licenses transparent replay.

The client resolves an Execution profile once from an explicit choice, saved preference, or advertised configured default and sends the explicit choice to Trusted open. A failed choice does not fall back. Restart does not recreate live Sessions or their Current study. See the Client Session contract.

Public datastore object references use {datastore_id, kind, id}. Here datastore_id is the durable Datastore UUID, not a configuration key or a connection address. Dataset and Datafile references identify logical Assets; an asset_version reference identifies an immutable revision. References carry scope and type, but grant no access. Names remain convenience selectors: ambiguous names and conflicting Study, Domain, type, or version constraints fail explicitly. Permitted latest-version selections resolve once before planning or admission. See ADR-0025.

Workflow Ownership

Developer View: Application Layer Detail

ahri_tre_app owns the substantive Session and Browser use cases. Trusted adapters authenticate callers, retain capabilities, validate transport framing, and map requests and results. PostgreSQL adapters own statements, row decoding, transactions, and persistence mechanics. The Lake adapter owns DuckDB, container-visible Lake locations, trusted scratch, and physical cleanup.

The workflows cover catalogue, Study/Asset governance, semantic definitions and instances, mappings and links, dictionaries, provenance, archive-first deletion, acquisition, Dataset writers, and governed content. Semantic values and inferred dictionary content use the same disclosure rules as Dataset content; metadata read permission alone does not admit content-derived results. Browser Discovery uses its own restricted safe projections and cannot borrow analyst authority.

Study Access-request Workflow

Web validates its browser session and request-security context, then forwards protocol intent over its authenticated service connection. Trusted validates ready-Datastore scope and supplies narrow application ports through ahri_tre_runtime_pgmeta:

  • Discovery uses a read-only metadata authority.
  • Requester submission, listing, detail, and withdrawal use a separate least-privilege access-request authority and the server-derived visitor.
  • Custodial queue, approval, and rejection use the matching live custodian Session’s libpq OAuth connection and authenticated PostgreSQL actor.

For approval, the application preflights the pending request, provisions or reuses the requester’s workflow entry in that exact Datastore, then records the Study grant and approved decision through the authenticated metadata port. Trusted resolves the workflow-entry Secret at the newly authorized operation boundary. Web owns neither that Secret nor a database connection.

Workflow entry is an idempotent prerequisite, not a Study grant. If provisioning succeeds and the final decision fails, the entry remains for a safe retry; compensating deletion cannot prove that another authority does not use it. Submission and withdrawal grant neither Study access nor workflow entry.

Dataset Writers and Tracked Operations

All supported Dataset writers share durable Output reservations for the Datastore, Output study, and logical Dataset. They contend for the same target across users, Sessions, and name/reference selectors before version allocation or materialization.

flowchart LR
    authorize["Resolve and authorize intent"]
    reserve["Reserve logical Dataset<br/>persist attempt ownership"]
    prepare["Prepare immutable sources<br/>materialize Lake output"]
    admit["Atomic PostgreSQL admission<br/>metadata, provenance, latest version"]
    success["Consume reservation<br/>return safe result"]
    cleanup["Compensate known failed work"]
    release["Release failed attempt's reservation"]
    retain["Retain ownership<br/>until cleanup and executor exclusion are proven"]

    authorize --> reserve --> prepare --> admit --> success
    prepare -->|failure| cleanup
    admit -->|known rollback| cleanup
    admit -->|unknown commit outcome| retain
    cleanup -->|incomplete or executor still active| retain
    cleanup -->|complete and executor excluded| release

Final Dataset metadata and successful provenance commit together. Lake work remains outside that transaction. Unknown commit outcomes retain output and ownership; cleanup must not destroy possibly admitted data. A failed or expired operation alone does not make the target reusable. Successful admission consumes the reservation only when remaining work cannot mutate its committed target.

Managed Datafile-to-Dataset materialization has public Operation tracking, including admission idempotency, status, cancellation, history, and durable result readback. Start acceptance persists its reservation, attempt, pending Operation, and optional idempotency binding atomically; final admission records the typed result in the metadata transaction. Cancellation is cooperative and cannot retract a committed result. Restart interruption requires reconciliation and explicit new work, not automatic Session restoration or materialization replay. Other synchronous writers reuse reservation and compensation ownership without pretending to be detached Operations. See ADR-0018.

Source Acquisition and Governed Derivation

Clients may read an external source with their own authority and upload it, or securely delegate a bounded acquisition to the Trusted runtime. Destination Study authorization is independent of source authentication. A workstation path is opened by the client and never becomes a server path to reopen.

The implemented acquisition families include local files and Arrow/table uploads, HTTPS, client-local DuckDB/SQLite, client or Trusted PostgreSQL/MSSQL, and offline or live REDCap. Input formats and provider restrictions are defined by the upload, HTTPS, SQL source, and REDCap contracts.

User-supplied source credentials travel separately from public JSON and are retained only for that acquisition. They are discarded on completion, failure, cancellation, or expiry, with no Session credential cache or authenticated pool for later work. Named shared sources have separate configured grants and logical Secret references; ordinary Study access does not grant their credentials. Outbound policy, TLS verification, finite budgets, and parser limits apply before admission. TRE metadata credentials never substitute for source credentials.

Ordinary ingest requires explicit Asset risk classification. Low classification requires the owning Study’s custodian and justification; later versions must respect the logical Asset’s classification. Uploaded or client-produced content is not proof of a governed derivation.

dataset.transform is a bounded synchronous governed SQL writer. It resolves all input dependencies and an independent Output Study, evaluates captured immutable inputs in the restricted query worker, and admits high-risk output through the shared writer. It returns safe identities and provenance, not rows or derived counts. It is distinct from both external SQL acquisition and direct low-risk query disclosure. See the Dataset SQL transform contract.

Trusted Content Disclosure

The application resolves complete immutable inputs, including dependencies behind query views and aliases, checks authorization and risk, and captures the admission snapshot. Unknown dependencies, missing classification, unauthorized inputs, or any high-risk input prevent content disclosure to a user-controlled client.

Authorized low-risk content may be returned completely within finite service budgets; those budgets protect reliability rather than create a second disclosure grant. Dataset data, previews, exports, Datafile bytes, query results, and content-bearing semantic responses share this boundary.

Required admission/start evidence is durable before the first byte leaves Trusted control. The protected Governance Study’s append-only, non-content ledger records governance evidence. Delivery completion records outcomes and known rows/bytes without copying content. An unresolved older completion retains recovery state; each new disclosure still needs its own durable admission. Reclassification or revocation blocks new admissions but does not retroactively truncate an already admitted finite stream or recall an earlier client copy.

Trusted transport delivers framed payloads with identity, sequencing, digest, and terminal-evidence validation. EOF without a verified successful terminal is failure. CLI file destinations publish atomically only after verified completion; stdout may already contain partial bytes when a transfer fails. C results support bounded incremental reads or explicit bounded materialization, with independent result-buffer ownership. Managed buffering creates no implicit persistent cache. See the content transfer contract.

Lake locations, storage credentials, and trusted scratch stay inside the trusted boundary. Clients identify returned copies as low-risk / user-controlled; this notice is not another approval step. Legacy unclassified Assets require the explicit preserving governance upgrade, which classifies them high and records evidence while retaining safe metadata access.

Crate Boundaries

LayerCratesResponsibility
Domainahri_tre_types, ahri_tre_coreHandle-free records, identifiers, value objects, policies, and repository contracts.
Applicationahri_tre_appWorkflow decisions, authorized selection, governance, provenance, writers, disclosure, and compensation.
Configurationahri_tre_configVersioned documents, validation, selection, Effective graphs, fingerprints, and public Client projection.
Secretsahri_tre_secretsInjected snapshots, encrypted Managed storage, authorized resolution, and acquisition-only source material.
Runtime stateahri_tre_runtime, ahri_tre_sessionImmutable bootstrap, retained authentication/capability state, Managed transport, shared Client Session behavior, and safe local context.
Trusted compositionahri_tre_trusted_runtime, ahri_tre_runtime_pgmetaExecutable composition, authenticated dispatch, live Sessions, and narrow runtime-to-metadata capabilities.
Metadataahri_tre_pgmetaPostgreSQL schema, queries, read models, transactions, admission, reservations, and governance persistence.
Authenticationahri_tre_orcid, ahri_tre_libpq_oauthOIDC contracts/exchange and the separate PostgreSQL libpq OAuth bridge.
Lake and tabular dataahri_tre_lake, ahri_tre_tabularDuckDB/DuckLake, storage and scratch mechanics, restricted evaluation, Arrow, Parquet, and IPC.
Sourcesahri_tre_sqlmeta, ahri_tre_redcapExternal SQL and REDCap provider mechanics and metadata.
Public interfacesahri_tre_protocol, ahri_tre_cli, ahri_tre_daemon, ahri_tre_web, ahri_tre_ffi_cVersioned contracts and thin command, local lifecycle, Browser HTTP, and C ownership adapters.
Cross-cutting policyahri_tre_security, ahri_tre_observabilitySensitive-material projection/redaction and closed, bounded operational event contracts.
Qualificationahri_tre_bootstrap_acceptance, ahri_tre_remote_acceptance, ahri_tre_secret_acceptance, ahri_tre_dataset_acceptanceExecutable and integration evidence for the composed boundaries.
Binding scaffoldsahri_tre_python, ahri_tre_julia, ahri_tre_rTransitional workspace scaffolds; host-language packages consume the C ABI from companion repositories.

PostgreSQL OAuth retains libpq. The existing password-authenticated Browser Discovery adapter may use Rust postgres, but remains behind ahri_tre_pgmeta and is invoked from Trusted composition. Neither Web nor domain types acquire a database backend through that exception.

Datastore Identity Binding

There is no central registry database. A datastore-local binding records its Datastore identity, generated durable UUID, owned physical stores, encryption policy, credential references, and lifecycle state. The Datastore identity is the PostgreSQL metadata database name; the Datastore configuration ID selects an Application declaration; the public Datastore identifier is the generated UUID. These values have different purposes.

An Execution profile selects one configured Datastore. Trusted constructs the Session plan from retained configuration, opens the selected metadata database, checks its binding and DuckLake state, and passes the resolved capability to application workflows. A client supplies profile intent, not a physical database or Lake profile. Creation, adoption, and storage maintenance remain privileged workflows. Lake locations come from Effective configuration and verified binding state and must be container-visible; host-side Restricted local references belong to deployment tooling.

Metadata Identity Ownership

Ordinary new persistent identifiers are datastore-owned. Creation requests describe the object; metadata adapters omit generated IDs and return the persisted record. PostgreSQL identity sequences and UUID defaults provide the identities. Dataset and Datafile subtype rows use the datastore-assigned Asset-version ID rather than another application-generated identity.

Fixtures, import/replay paths, reference seeds, natural keys such as DUO codes, composite joins, and temporary staging names are deliberate exceptions. Public Datastore-bound references encode existing identities without requiring a wholesale primary-key migration.

Diagnostics and Qualification

Operational diagnostics report bounded safe observations with acquisition time and evidence scope. A retained Session-open observation is historical evidence, not a fresh database or Lake probe. Correlation IDs connect requests and Operations without granting authority or replay rights. Operational events omit credentials, content, raw SQL, private paths, and arbitrary adapter errors. Their bounded best-effort delivery is separate from the durable evidence required for governance and disclosure. See Diagnostics and Observability.

The completed parity PRD qualifies the integrated workflows through the recorded installed Linux aarch64 CLI/C artifacts and matching protocol-v2 Browser companion. The inventory distinguishes ordinary-user, Browser, operator, local, and bounded transfer surfaces. Actual capability advertisement also reflects deployment configuration, including whether tracked operation execution is enabled.

This evidence does not qualify every native platform or deployment image. Release handoff, large/slow ingest resume, and the separately planned concurrent Asset-access hardening retain their own backlog and validation scope.

Target Architecture: Trusted Runtime and Governed Compute

The Trusted boundary, disclosure policy, shared writer ownership, protected Governance ledger, and bounded restricted SQL evaluation above are implemented. The broader Governed-compute system in ADR-0009 remains a distinct target: a general job scheduler, arbitrary pipeline workers in a separate trust zone, reviewed fixed transformations, and governed release/publication workflows are not implied by Client parity or the current private query worker.

flowchart LR
    client["User-controlled Client / JupyterHub server"]
    trusted["Trusted runtime<br/>authorize and admit governed work"]
    worker["Governed compute worker<br/>isolated pipeline job"]
    output["Declared governed outputs"]
    release["Authorized Export / Repository transformation"]
    external["Approved external destination"]

    client -->|job intent| trusted
    trusted -->|explicit inputs and short-lived Job capability| worker
    worker --> output --> trusted
    trusted --> release --> external

The accepted target gives workers explicit read-only inputs and private staging, without raw Lake mounts, datastore credentials, Managed secrets, or unrestricted network access. Arbitrary user pipelines produce high-risk outputs; a reviewed fixed trusted transformation may derive low-risk output only under its immutable input/output and risk rule. High-risk content leaves TRE control only through an authorized release workflow. A low-risk input does not weaken the boundary when another input is high risk.

JupyterHub is a user-side client environment. Its trusted identity broker may project a bounded Runtime credential into the user server, while upstream OAuth artifacts and datastore authority remain protected. Image publication and qualification are separate delivery gates. Pilot, test, staging, and production are separate Deployments with independently provisioned identities, Datastores, Lake storage, and Secrets; promoting immutable software does not promote data or credentials.