What automatic unlock means
Automatic unlock delivers the same volume key on every authorized, non-debug enclave boot. Your private keyserver verifies the enclave, reads the key from your secret store, and releases it directly to the enclave. The guest must unlock and mount the disk before the workload starts. You do not send a decrypt command after each Deploy. This requires setup. A disk attachment, a named mount, or a saved CLI profile alone does not enable automatic unlock or prove that the filesystem is unlocked. Without an unlocked volume mounted at the application’s configured path, writes to the enclave’s ephemeral filesystem do not survive Stop followed by Deploy or replacement during Update. The CLI provisioning workflow uses customer-owned AWS Secrets Manager. It is not a Tinfoil-managed key service. You operate the private keyserver, its TLS endpoint, and its release policy. There is no shortcut that uploads the volume key to Tinfoil’s secret store.Prepare customer infrastructure
Before provisioning a key, arrange:- A customer AWS account, region, and secret-name prefix, with durable retention and a recovery plan for the original key. Do not enable automatic rotation for volume secrets.
- A reachable private keyserver with a publicly
trusted TLS certificate. Its
/challengeand/fetchendpoints must be served directly, without redirects. Keep it available for unattended boots. - A keyserver configured with the AWS backend, the same region and prefix as the CLI profile, and a customer-controlled policy loaded at keyserver startup.
- The exact config repository, intended release tag, and instance domain. The policy must pin all three; do not guess a domain or remove a pin to make a boot succeed.
- An application image and a declared named volume mount in
tinfoil-config.yml. Preparing unlock configuration does not replace your application image or copy ephemeral files to disk.
Separate provisioning from release permissions
Use separate customer identities for setup and the running keyserver:
Existing-key setup needs Describe/Get access, not Create/Tag. Reading an existing secret
encrypted with a customer-managed KMS key also requires
kms:Decrypt. New secrets use AWS’s
default Secrets Manager encryption key; the helper does not offer custom KMS key selection.
The customer grants permissions. Local preflight is not an AWS write-permission dry run:
Create/Tag authorization remains unproven until the actual CreateSecret call succeeds.
CLI configuration does not create IAM roles, grant access, or deploy the keyserver.
Scope permissions to the intended secrets and KMS key rather than
granting account-wide secret administration.
The CLI uses the AWS SDK to create a new secret, not a plaintext shell command. It does not
overwrite an existing secret with an update/upsert operation. Keep key values out of command
arguments, logs, Git, and support messages. AWS documents the permission requirements for
CreateSecret,
DescribeSecret,
and GetSecretValue,
and the exposure risks of plaintext command arguments.
Local profiles and key custody
A project storage profile is scoped to the normalized controlplane URL, authenticated organization ID, and canonical config repository. A per-volume receipt also binds the volume UUID. Switching organizations, controlplanes, or repositories does not reuse another scope’s profile. Setup requires an organization login, not a personal context. Profiles and receipts contain metadata, not volume keys or AWS credentials. An AWS profile name is only a reference to your AWS credential configuration. The files live in a separatestorage/ directory beside the effective CLI config file; the directory uses mode 0700 and
JSON files use mode 0600. Login and logout do not rewrite this storage metadata.
Only new auto-unlock volume creation generates a random 64-byte key. The key is stored in
AWS Secrets Manager as a JSON string with exactly one field, value, containing the standard
base64 encoding. Plain volume creation does not generate a key.
For an existing disk, supply a reference to the original key already stored in that format
under the configured AWS prefix. If the original key is held elsewhere, import those exact
bytes through your customer-controlled secret-management process first. Do not print them or
put them in shell arguments. Selecting an existing secret validates its format and identity,
not whether it can decrypt the disk. Rotation must be disabled.
Configure once per project
These helpers require a CLI build withproject storage configure, volume auto-unlock,
and volume create --auto-unlock, in addition to the lifecycle prerequisites.
The published v0.18.9 CLI does not include them. Until a release contains them, use a build
supplied for the Containers rollout. Confirm tinfoil project storage configure --help and
tinfoil volume auto-unlock configure --help show those exact commands, and that
tinfoil volume create --help lists --auto-unlock.
With an organization admin login, save the provider metadata:
owner/repo works before the first instance exists; an existing project ID also
works. The CLI verifies organization identity and repository access without creating an instance
or project. Repeating identical setup is safe; different saved custody metadata is refused.
With --output json, setup reports configuration: PROFILE_SAVED and
policy: not_applied; neither field means AWS access or remote approval was verified.
The helper requires an HTTPS origin, without a path, credentials, query, or fragment.
Configure your keyserver with BACKEND=aws, the same AWS_REGION, and
AWS_SECRETS_PREFIX matching --aws-prefix. Use --aws-prefix="" explicitly for no prefix.
The CLI and keyserver must access the same customer AWS account and region; policy resolves
secrets by name under that prefix, not by a cross-account ARN.
Omit --aws-profile to use the standard AWS credential chain. Vault and file provisioning
are not implemented by this helper.
You can save an exact domain with --domain; otherwise supply it during volume setup.
Missing required values prompt only in an interactive terminal. Scripts must supply them.
Prepare a volume
Use your actual localtinfoil-config.yml, retaining its application image and other settings.
The example assumes a top-level volume named data already exists and is mounted by your
application. --mount data selects that name, not a filesystem path.
Choose a new config release tag to publish after reviewing the generated config;
v1.2.3 below is an example, not a guest version. Supply the exact known instance domain
(or a verified custom domain you will use), not a guessed hostname. The helper’s --domain
pins policy; it does not configure DNS or assign a domain to an instance.
New disk: generate the key once
tinfoil/volumes/<UUID>/key; the generated private reference is
VOLUME_<UUID_WITH_UNDERSCORES>_KEY.
Existing disk: select only the original key
Do not run the new-disk command for an existing volume. Use its UUID and original secret:--existing-secret is a name or full ARN, never the raw or base64-encoded 64-byte key. Do not
paste key bytes into command arguments: shell history and process arguments can expose them
before any CLI validation. The reference is required even when local metadata is missing.
The CLI rejects recognizable raw-key encodings before network requests or metadata writes;
this cannot undo exposure in shell history or process arguments.
This path reads the original secret; it does not generate or rotate a key.
The selected secret must be under the configured prefix. An ARN is resolved to its actual
name before the policy path is derived.
An imported secret must belong to this disk and storage scope. Do not reuse one tagged for
another volume or change its provenance tags to bypass a mismatch. Format validation alone
cannot establish that an untagged imported key is the original key for the disk.
Review the artifacts
The helper adds a missingkeyserver-url and the selected mount’s key-secret, retaining an
existing disk’s declared secret reference. It preserves other config fields and comments and
refuses conflicting values. The policy pins the repository, tag, and domain, and maps the
private reference to the AWS secret’s path and value field. Key bytes are not in either file.
Pass --policy-file policy.yml to prepare an additive copy of an existing policy. Without
that input, prepared-policy.yml is a fragment for manual review and merging, not a
replacement for the keyserver’s full policy. Include mappings for all private secrets the
release requests, not only the volume key.
Setting keyserver-url selects private delivery for all declared container, model, and volume
secrets in a non-debug instance. This helper provisions only the selected volume key. Store
the other required values in your customer secret store and map them in the reviewed policy;
missing values fail boot rather than falling back to Tinfoil-managed secrets.
Input and output paths must differ, output parent directories must exist, and new-volume
outputs must not already exist. Existing-volume setup can reuse identical outputs but refuses
to overwrite different contents. Choose fresh output filenames when preparing a new release.
Keep approval explicit
The CLI prepares local configuration and policy artifacts. It does not remotely approve a release, install or reload policy, deploy a keyserver, or observe a guest filesystem unlock. Addingkeyserver-url or volumes[].key-secret changes measured configuration. Review the
config, publish a new measured release, and authorize that exact repository, tag, and
domain on your keyserver before Deploy. Editing local YAML does not change an already
published release. Review and install the policy through your own deployment process, then
restart the keyserver so it loads the policy at startup.
Propose the prepared config, then review and merge the returned pull request:
Authorize another release without changing the volume key
Reuse the existing volume UUID and original secret ARN when preparing another application release. First edit./tinfoil-config.yml to contain the intended new release’s changes,
including its intended image. Preserve keyserver-url, the selected mount’s name and
key-secret, and the application’s persistent mount mapping. Merely assigning a new tag to
an unchanged v1 config does not update the application image.
Use a trusted local copy of the currently installed full keyserver policy as
./installed-policy-v1.yml. In this example, it contains the existing release’s approval,
and v2.0.0 is the new config release tag you intend to publish:
volume-<UUID>; when that
name is occupied, it uses a deterministic suffix containing the full SHA-256 hash of the
repository/tag/domain tuple. A conflicting derived name is refused, never overwritten.
The name is not the authorization boundary: the exact pins still apply. Repeating preparation
with the same inputs produces byte-identical outputs without duplicate entries.
A new approval maps only the selected volume key. Review and add mappings for any other
private secrets the new release needs; they are not copied automatically from the old approval.
The one-domain-per-repository/tag restriction still applies.
Review the generated config and open its pull request:
./prepared-policy-v2.yml through your keyserver deployment process,
then restart the keyserver to load it. Only then Update the instance
to the new tag, retaining the same disk and original key. The CLI neither installs the policy
nor verifies that it was loaded; CONFIGURED_LOCAL is not remote approval or observed unlock.
Adding v2 does not revoke v1. The old release remains authorized until you explicitly remove
its policy entry and reload/restart the keyserver. Retain that approval through the rollback
window and while any instance may still need to boot or Deploy the old release. Retiring an
approval is a separate operator decision, not an automatic part of preparing v2. Do not delete
or rotate the volume key when retiring an approval.
What each phase proves
configuration field can be:
CONFIGURED_LOCAL: the saved metadata and prepared artifact hashes agree.INCOMPLETE: setup stopped before both artifacts were recorded; inspect the recovery phase.STALE: an artifact is missing or changed, or local metadata disagrees.UNKNOWN: this scope has no profile or receipt; it does not mean the disk is locked or its key is lost.
phase records allocated, secret_create_attempted,
verifying_existing_secret, secret_stored, or prepared. These are setup checkpoints,
not remote deployment or unlock states. Failed secret verification marks reconfiguration
incomplete. Artifact conflicts preserve the prior receipt; they do not confirm that the new
release has been configured successfully.
The JSON unlock field is not_observed. Even CONFIGURED_LOCAL carries
awaiting publication/policy load; unlock not observed. The CLI does not confirm remote
publication or policy loading, so completing those steps does not turn this into an unlock check.
Confirm the workload’s unlock result and that its durable-data path is backed by the expected
unlocked filesystem before relying on writes. Keyserver HTTP health alone is not unlock proof.
Stop, Deploy, and recovery
Stop followed by Deploy retains the disk and its saved attachment. Reuse the actual disk, the intended measured application release, and the original key. Do not substitute an example image, create another disk, or generate a replacement key to recover data. Preserve a backup of the original key outside the enclave, and keep the authorized private keyserver available. For an Update, publish and authorize the new measured release while retaining the same disk and key. Updates with persistent volumes replace the running enclave and require downtime. If setup fails after allocation, retain the reported volume UUID, secret reference, and phase. Use the existing-disk command with that original reference and reviewed artifact flags; do not retry by creating a replacement disk. Recovery checks the original secret’s identity and version and does not generate another key. If the first key never reached AWS, setup remains incomplete and needs operator investigation. If allocation itself has an uncertain outcome, inspecttinfoil volume list before retrying.
Debug mode is not automatic private unlock. It ignores keyserver-url and uses host-supplied
managed secrets. Never expose a production volume key or use a production disk in debug mode;
use disposable test disks and test secrets instead.
Manual unlock is an advanced alternative
Omitkey-secret only if your application implements the supported runtime unlock flow.
The application uses the volume’s control socket at
/run/tinfoil/volumes/<mount-name>/control.sock; this is an application integration, not a
generic CLI unlock command. Verify the enclave before delivering the original key, and wait
for successful unlock before using the mounted path for durable writes.
Boot-time private-secret unlock is supported with cvm-version 0.14.4 or later. The
locked-volume write guard makes a locked
mount’s placeholder read-only, but requires a newly published measured guest release and
an explicit update of the approved guest release/measurement pin. That release has not been
published; do not assume the 0.14.4 minimum includes the guard or rely on it for write protection.
Installing the CLI does not publish a guest image, deploy it, or update
runtime pins.
The guard prevents writes from silently landing in ephemeral storage at a locked volume path.
It does not make other application paths persistent. Files outside the unlocked mounted
filesystem remain ephemeral across Stop and Deploy or replacement during Update.
