Managing host credentials
For a pinned repository task using selected env/file credentials, see Private tracked runs, including CLI/Python submission, recovery and current availability.
For an agent-led checkout and credential provisioning workflow, see Setting Up Remote Agent Environment.
Available in CLI 0.13.0 and Python 0.10.0 or later. The hosted vault uses managed AWS KMS without requiring your own cloud account. Entries, scoped access keys, selected SSH/TOTP import and TOTP generation are released; installed-client production checks passed. The web Vault supports entry and scoped-key management, expiry and write recovery. See the runtime note for deployment evidence and remaining acceptance gaps.
Host enrollment saving is available in CLI 0.16.0, including its npm wrapper, and Python 0.13.0 with a compatible backend. Hosted staging acceptance passed; production host saving is a separate rollout. Saving follows enrollment, so saving failure does not undo an online host. The published npm 0.16 wrapper passed target/jump password-descriptor saving; upgrade from the affected 0.15 wrapper. Published CLI 0.18.0/Python 0.15.0 key rotation has hosted staging acceptance. Password lifecycle, broader remote failure coverage and production KMS/retention obligations remain separately tracked in issue #241.
Guides and deliverables
- Runnable CLI/Python examples: checkout setup, entry operations, scoped keys and credential imports.
- Isolated server: explicit backend/Mongo/KMS configuration for synthetic accounts.
- Live smoke runner and results: bos14 AWS KMS roundtrip, client suites, failure handling and cleanup evidence.
- Terraform test setup: isolated plan/apply/test/cleanup commands. Includes reviewed plan/apply, readiness and guarded cleanup. Historical acceptance retained billable infrastructure; check the current state before reuse.
- Kubernetes fixture and Pod Identity IAM setup: merged in examples PR #14; see the EKS results.
- Vault design and remaining work: host credential selection, sync, lifecycle and KMS policy requirements.
The linked results record synthetic-account tests on bos14 and a Terraform-managed EKS cluster with real AWS KMS, client regression and failure checks, and fixture cleanup. These historical runs are separate from hosted production acceptance and do not establish live GCP readiness.
Personal entries live under your owner name, such as ge/. Account permissions control access; no device pairing is required. Customer KMS and BYOC backend setup are optional operator concerns.
Inspect the owner-only policy tree
The policy tree shows metadata for your owner prefix, including the governing key and migration state, without reading secret values. Sign in as the owner; scoped access keys cannot use this view.
Python 0.17.0 is published on PyPI; CLI 0.22.0 publication is incomplete. Use the reviewed CLI candidate or published Python package with a backend that supports the tree route. A missing route is an error, not an empty tree. See the runtime development note for compatibility and acceptance evidence.
# Explicit personal owner prefix; a scoped key cannot use this policy view.
dreamlake vault list -p ge/training --tree
# One metadata page for scripts; ordinary vault list JSON is unchanged.
dreamlake vault list -p ge/training --tree --to-json --limit 100from dreamlake import RemoteClient
# Uses the securely saved login for this API origin.
client = RemoteClient("https://api.dreamlake.ai")
page = client.vault.tree(prefix="ge/training", limit=100)
if page["nextCursor"]:
next_page = client.vault.tree(
prefix="ge/training", limit=100, cursor=page["nextCursor"],
)In the terminal use arrows to select, collapse or expand, n for the next page,
and q to quit. Pages are fetched explicitly and do not form a frozen whole-tree
snapshot. JSON preserves detail truncated by terminal width. Prefixes and entries
remain distinct; governing policy/key metadata does not prove which key encrypted
historical ciphertext. Unknown policy or migration state remains unknown.
This view does not reveal values, change policies, provision credentials or preview
cross-boundary moves. --include-deleted is unsupported.
Runnable tree example and local HTTP/Mongo/PTY evidence, CLI artifact receipt and Python archive receipt record exact tested versions. Python publication was separately verified against both frozen archives; a fresh no-cache PyPI install and production metadata-only tree check passed. The initial read-only install check failed without an established cause; its successful retry is recorded separately. CLI npm/native publication remains unproven.
Save one file in the web Vault
Open your profile's Vault tab, choose New entry (or Replace value), keep String, and choose one UTF-8 file. Selection reads that file locally. Review its basename, byte count, destination and expiry, then explicitly save. Manual input stays masked until Reveal secret input. No adjacent SSH keys, aliases or jump-host credentials are discovered, and saving does not enroll a host or install a remote key.
The file must be nonempty, valid UTF-8, no larger than 64 KiB, and contain no NUL or unsupported control bytes. Its JSON-encoded value must also fit 64 KiB. BOM, CRLF and trailing whitespace remain unchanged; revealing alone does not rewrite them. Editing visible text uses browser newline handling. Closing, leaving, changing accounts or canceling discards pending reads. Unknown writes use the existing receipt recovery flow without automatically retrying uploads. Web export/download and full remote setup review remain separate work.
Staging and production evidence records desktop/mobile saving and cleanup. Equivalent explicit client operations:
# The selected CLI input here is UTF-8 without a BOM.
dreamlake vault add -n ge/remote-agent/key \
--stdin --file-name id_ed25519 < ./id_ed25519
dreamlake vault show -n ge/remote-agent/keyfrom pathlib import Path
source = Path("./id_ed25519")
# read_bytes avoids newline normalization; no implicit prompt or secret logging.
entry = client.vault.add(
name="ge/remote-agent/key",
value=source.read_bytes().decode("utf-8"), file_name=source.name,
)
metadata = client.vault.show(name="ge/remote-agent/key")Read and compose
list returns metadata; get explicitly returns plaintext values. Python uses an authenticated client and never prompts automatically. Keep retrieved values out of logs and agent transcripts.
dreamlake vault list -p ge/training
dreamlake vault get -n ge/training/storage --to-json
dreamlake vault get -n ge/training/storage.access-key
# Compose entries using their saved environment names.
dreamlake vault -p ge/training get \
-n ssh-key -n tracking-token --to-envsentries = client.vault.list(prefix="ge/training")
storage = client.vault.get(name="ge/training/storage")
access_key = client.vault.get(name="ge/training/storage.access-key")
exports = client.vault.get(
prefix="ge/training", name=["ssh-key", "tracking-token"], to_envs=True,
)Use path.field for field selection and repeated -n for composition. -p/--prefix abbreviates paths for one invocation and can appear before or after subcommands. Leading / bypasses abbreviation; conflicting prefixes fail. --to-json and --to-envs are exclusive. Resolve the whole request before returning values; failures emit no partial secret output.
An entry can save output metadata:
Ordinary secrets are strings or named string fields. totp adds code-generation behavior; reading a seed and generating a code are separate operations. CLI 0.16/Python 0.13 support HOTP with a compatible backend; hosted staging acceptance and production rollout are separate. keyName names a value in composed JSON, env names a single-value entry's environment variable, and fileName is a suggested basename for explicit file export. It never writes a file automatically.
For env output, an explicit alias such as -n TOKEN=ge/tracking.value wins. A selected field uses its normalized field name; a whole scalar entry uses saved env, then its normalized basename. Reading a whole map exports its field names, not the entry-level env. --env-prefix changes output names only. Reject invalid names/collisions rather than silently renaming them. Structured entries use field mappings, not one ambiguous entry-level env name.
Supply values to a program
--to-envs emits quoted POSIX shell exports. It cannot change the parent shell itself. Check retrieval before evaluating output and use a subshell when variables should live only for the job. Do not enable shell tracing around secrets.
(
exports=$(dreamlake vault get -n ge/tracking-token --to-envs) || exit
eval "$exports"
unset exports
python train.py
)
# When the consumer accepts stdin; use a shell supporting pipefail.
set -o pipefail
dreamlake vault get -n ge/tracking-token | client-tool --token-stdin
# For an argv-only consumer, check lookup failure first.
token=$(dreamlake vault get -n ge/tracking-token) || exit
legacy-client --token "$token"
unset tokenimport os
import subprocess
token = client.vault.get(name="ge/tracking-token")
subprocess.run(
["python", "train.py"],
env={**os.environ, "TRACKING_TOKEN": token}, check=True,
)
subprocess.run(["client-tool", "--token-stdin"], input=token, text=True, check=True)
subprocess.run(["legacy-client", "--token", token], check=True)Consumer programs here are illustrative. Argv may be visible to process inspection; stdin is preferable when supported. Command substitution strips trailing newlines, so use JSON/stdin or Python for exact multiline values. A receiving process can copy or log secrets; delivery is not a secrecy boundary against that process.
Save values and import selected TOTP entries
add uses a masked prompt on a TTY; noninteractive callers provide stdin explicitly. Python receives the securely collected value and never prompts. show returns metadata only. Host enrollment can save selected credentials; see Enrollment. Saving and remote key installation are separate operations.
# Collect a value through a masked TTY prompt, never as an argument.
dreamlake vault add -n ge/bos14-login
dreamlake vault show -n ge/bos14-login
# Preview only: use an explicit canonical store path.
dreamlake vault import --pass-otp -p ge --dry-run --store /absolute/pass-store# Caller supplies securely collected secret; no implicit prompts.
client.vault.add(name="ge/bos14-login", value=secret)
metadata = client.vault.show(name="ge/bos14-login")
preview = client.vault.import_entries(
source="pass-otp",
store="/absolute/pass-store", prefix="ge", dry_run=True,
)Preview uses noninteractive GPG and an explicit canonical store path. It decrypts locally but uploads nothing, excludes adjacent passwords, and never generates codes or advances HOTP counters. Paths must not traverse symlinks.
Choose canonical pass paths without .gpg from the preview. Only selected OTP registrations are uploaded; adjacent passwords are excluded. TOTP registrations can generate codes; HOTP registrations are inactive until DreamLake explicitly takes counter ownership. On a TTY, review and confirm the upload. Explicit --select supplies consent for noninteractive use; Python requires selection and never prompts.
dreamlake vault import --pass-otp -p ge --store /absolute/pass-store \
--select accounts/example
dreamlake vault otp -n ge/accounts/exampleresult = client.vault.import_entries(
source="pass-otp", prefix="ge", store="/absolute/pass-store",
select=["accounts/example"],
)
code = client.vault.otp(name="ge/accounts/example")OTP generation requires owner authentication; scoped keys cannot generate codes. Treat generated codes as secrets. Import does not overwrite existing OTP entries. If an upload outcome is unknown, stop and inspect it; do not blindly repeat the import.
For HOTP, stop every other generator before activation. Import is inactive by default; inspect the entry revision before activating it. The CLI example below uses revision 1 for a newly imported entry; substitute the actual inspected revision. Python uses the returned revision directly. Activation does not generate a code or update the local pass store.
dreamlake vault import --pass-otp -p ge --store /absolute/pass-store \
--select accounts/hotp-example
dreamlake vault show -n ge/accounts/hotp-example
dreamlake vault otp -n ge/accounts/hotp-example \
--activate --counter-owner dreamlake --if-match 1
mkdir -m 700 -p ./otp-requests
dreamlake vault otp -n ge/accounts/hotp-example \
--request-file ./otp-requests/hotp-example-001.jsonclient.vault.import_entries(
source="pass-otp", prefix="ge", store="/absolute/pass-store",
select=["accounts/hotp-example"],
)
metadata = client.vault.show("ge/accounts/hotp-example")
client.vault.activate_otp(
"ge/accounts/hotp-example", counter_owner="dreamlake", if_match=metadata["revision"],
)
from pathlib import Path
Path("otp-requests").mkdir(mode=0o700, exist_ok=True)
code = client.vault.otp(
"ge/accounts/hotp-example",
request_file="./otp-requests/hotp-example-001.json",
)Run these examples in a writable, user-owned directory. The otp-requests directory must be private (mode 0700), including when it already exists. Each new HOTP issuance uses a new private request file. After an uncertain response, reuse the same file to recover that issuance; never substitute a fresh file. The durable intent contains identity and revision metadata, not the seed or code. Recovery expires after 24 hours; a missing or expired receipt does not prove that no code was issued. Initial activation supports SHA1 with at least a 128-bit seed; unsupported algorithms remain inactive. See the HOTP ownership and recovery reference. These commands are supported by published CLI 0.20.1 and Python 0.16.1; they require the corresponding server capabilities.
SSH import: select entries before uploading
Run from an authenticated interactive terminal:
The checklist starts empty. Select profiles and private keys separately, then review the server and vault destinations before confirming upload. Selecting a profile does not select its key or jump-host entries. Empty selection or cancellation before upload writes nothing; cancellation during upload preserves completed entries and stops further uploads.
Preview discovery without reading private keys or contacting the vault:
For noninteractive use, pass the item IDs shown by discovery explicitly. Python uses explicit selection and never opens a checklist:
dreamlake vault import --ssh -p ge/remote-agent \
--select profile:dev-box --select key:dev-box:1 --jsonpreview = client.vault.import_entries(source="ssh", prefix="ge/remote-agent", dry_run=True)
result = client.vault.import_entries(
source="ssh", prefix="ge/remote-agent",
select=["profile:dev-box", "key:dev-box:1"],
)Use --config for a specific SSH config file. Discovery does not expand Include
files. Compatibility aliases remain vault ssh sync and vault pass sync --otp.
Unknown writes are reconciled only within the same bounded retry call. Do not blindly start a new import after an unknown outcome.
Host enrollment and explicit password saving are available in the published clients; see the enrollment guide. Remote password mutation, rollback and destination revocation require separate acceptance; a saved credential or a revoked Vault key does not prove access was removed from the remote host. Enrollment must succeed independently of saving; interactive saving defaults to [y/N], and quiet mode never grants consent.
Access keys and retirement
Access keys authenticate scripts to selected vault entries; they are not SSH credentials or agent objects. Reuse appropriate keys instead of creating permanent entries per worker. Renewal is policy-checked, and one-time means one atomic successful retrieval request. Expired/revoked keys cannot renew themselves; one-time keys are nonrenewable.
dreamlake vault keys create -n ge/training/storage --ttl 1h --renewable \
--request-id training-key-1 --token-file ./training-vault.key
dreamlake vault keys create -n ge/bootstrap --ttl 10m --one-time \
--request-id bootstrap-key-1 --token-file ./bootstrap-vault.key
dreamlake vault delete -n ge/bos14-login
dreamlake vault restore -n ge/bos14-login# Results redact bearer material in repr; provision .token securely once.
key = client.vault.keys.create(name="ge/training/storage", ttl="1h", renewable=True,
request_id="training-key-1")
once = client.vault.keys.create(name="ge/bootstrap", ttl="10m", one_time=True,
request_id="bootstrap-key-1")
client.vault.delete(name="ge/bos14-login")
client.vault.restore(name="ge/bos14-login")Token files must be new and are created with mode 0600. Retain the request ID when retrying uncertain issuance: replay returns metadata without re-revealing the token. Revoke and replace a key whose token was lost. Public GC commands and remote destroy remain proposed.
Deletion sets deleteAt, blocks new reads, and retains encrypted data until purgeAt. Restore during retention does not renew expiry or reinstall a revoked host key. Internal purge rechecks lifecycle state before hard deletion. Scheduled cleanup of terminal access-key metadata is deployed; it does not purge entry ciphertext or revoke remote host access. Scheduled entry/binding cleanup has isolated EKS acceptance. The hosted retention fixture retains its captured October 15 deadlines; hosted physical purge and backup coverage remain unproven. Intentionally unbound entries are not garbage.
Keep four lifetimes distinct: host access, vault availability, account/client cache, and retained storage. Vault expiry cannot invalidate a copied static password. Rotate by verifying new access before retiring old access. The proposed future destroy flow would combine remote revocation with soft retirement and later purge, keeping unreachable hosts pending; it is not an available command. Never claim deletion recalls downloaded secrets or ends existing SSH sessions.
Web management and uncertain writes
In the personal Vault page, create or replace entries, reveal/hide values, set or remove UTC expiry, and confirm retirement or restore. Scoped-key controls support selected-field issuance, renewal and confirmed revocation. Revealed entry values clear after 30 seconds or at expiry, whichever is first. Expiry does not retire an entry or revoke a copied remote credential.
If a create/replacement response is lost, Entry write recovery retains the request ID across reload. Check result reads the historical receipt; Open current entry metadata shows later changes separately. Forgetting an ID only removes the browser reference. A missing receipt does not prove a write failed. CLI/Python can look up a retained request ID without revealing values:
dreamlake vault write-status --request-id saved-write-idreceipt = client.vault.write_status(request_id="saved-write-id")Use the original ID from the uncertain write. Do not start a new write to test whether the old one committed. See recovery behavior and hosted evidence.
Prefix policies and agent behavior
Prefix KMS management and migration have implementation and bounded live cloud evidence; see the Prefix KMS note for verified scope and remaining hosted/operator acceptance, and the operator setup reference. Ordinary hosted users use managed KMS without cloud setup.
When using this guide as a skill, check installed capabilities and the target endpoint first. Keep secrets out of transcripts. Report provisioning, login verification, enrollment, saving and remote cleanup separately; do not infer credential export or removal of existing access from a setup request. Report unsupported operations rather than simulating them with plaintext files.