Workspaces

Storage connections

A storage connection is your own S3 bucket or Azure Blob container a new workspace can keep its files in, instead of Nace's managed storage. See Use your own storage for the concept, and the API reference for every field.

  • A connection names one destination: an S3 bucket and region, or an Azure storage account and container, with an optional base prefix. Each workspace gets its own folder beneath it. The destination can't change.
  • Who can do what: any member of the account lists and reads connections and binds a ready one to a new workspace. Only a connection's creator, or an owner or admin of the account, changes it; anyone else fails with 403 access_denied.
  • Cost: managing connections costs nothing and works at any balance.
  • Availability: a deployment that doesn't offer customer storage fails every command with 404 storage_connection_unavailable.

A connection's status is one of:

StateWhat you can do
draftSet it up, save credentials and validate it. It can't hold a workspace yet.
readyGive it to a new workspace. Validated: Nace can write, read and delete in the destination.
degradedA later check failed. Fix the access in your cloud and validate it again.
revokedNace stopped using it, and workspaces in it stop reading and writing. Restore one you revoked with nace storage validate --reactivate.

A connection is set up in five steps:

  1. nace storage create makes a draft.
  2. nace storage setup prints what to create in your cloud: for S3, an IAM role trust policy carrying the connection's external id, its permissions policy, a CloudFormation template and a quick-create link; for Azure, the az commands that give a service principal access to the container. Pass your AWS account id and role name, or your Azure subscription and resource group, and the setup comes back filled in.
  3. Create the role or service principal in your cloud.
  4. nace storage credentials saves the role's ARN, or the service principal and its secret.
  5. nace storage validate --wait checks that Nace can write, read and delete in the destination. Once it succeeds, the connection is ready.

A new IAM role or Azure role assignment can take a few minutes to apply, so a fresh one may fail its first validation: wait and validate again. Then pass the connection's id to nace workspace-create --storage; see Create a workspace.

nace storage create --name "Audit evidence" --provider s3 --bucket acme-audit-evidence --region us-east-1 --prefix nace
nace storage setup --id <connection_id> --aws-account-id 444455556666 --role-name nace-workspaces
# open the launch url it prints, and create the stack
nace storage credentials --id <connection_id> --role-arn arn:aws:iam::444455556666:role/nace-workspaces
nace storage validate --id <connection_id> --wait
nace workspace-create fy25-audit --storage <connection_id>

Every storage command is nace storage followed by an action, and takes its inputs as flags: the connection's id as --id, never a bare argument. nace storage alone exits 2 and lists the actions.

CommandWhat it does
storage listList your account's connections.
storage get --id CONNECTION_IDPrint a connection and its state.
storage create --name NAME --provider s3Create a draft connection.
storage setup --id CONNECTION_IDPrint what to create in your cloud, saving the setup inputs you pass first.
storage credentials --id CONNECTION_IDSave the role or service principal Nace uses.
storage validate --id CONNECTION_IDCheck that Nace can use the connection.
storage revoke --id CONNECTION_IDStop Nace using the connection.
storage delete --id CONNECTION_IDDelete a connection no workspace uses.

List storage connections

nace storage list
# storage connections (2):
#   <connection_id>  ready  s3://acme-audit-evidence/nace  Audit evidence
#   <connection_id>  draft  azure://acmeaudit/evidence  Audit evidence container

nace storage list prints storage connections (<n>):, the number of connections listed, then one line per connection, indented two spaces: <id> <status> <destination> <name>. The destination is s3://<bucket>/<prefix> or azure://<account>/<container>/<prefix>, without the prefix when it has none, and the status is one of those above. When more pages follow, the last line is next: <cursor>. Pass it as --cursor for the next page. An empty list prints storage connections (0):.

FlagWhat it does
--limit NConnections per page, 1 to 200 (default 50). A larger number exits 2.
--cursor CURSORStart at this page: the next_cursor of the page before.
--allEvery connection, across every page. Not with --cursor.
--jsonPrint {"items": [...], "next_cursor": ...}; next_cursor is null with --all.

Read a storage connection

nace storage get --id <connection_id>
# <connection_id>  ready  s3://acme-audit-evidence/nace  Audit evidence
# external id: nace-6b1f0c9e2d4a4e7b
# trusted role: arn:aws:iam::111122223333:role/ndi-api
# validated at: 2026-10-26T10:05:00Z
# ready for a workspace: yes

nace storage get prints the connection's line, as nace storage list prints it, then external id: and the id your IAM role's trust policy must require, a trusted role: line for each Nace role it must allow (none for Azure), a line for each of credentials pending, validation running and reactivation requires approval that is true, validated at: and revoked at: with the reason when they are set, and last ready for a workspace: yes or no. Only a connection that says yes can be given to nace workspace-create --storage. With --json, it prints the whole connection object. A connection your account doesn't have exits 1 with 404 storage_connection_unavailable.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id, as nace storage list lists it.

Create a storage connection

nace storage create --name "Audit evidence container" --provider azure_blob --account acmeaudit --container evidence
# <connection_id>  draft  azure://acmeaudit/evidence  Audit evidence container

nace storage create creates a draft connection and prints its line, or the whole connection with --json, and next: nace storage setup --id <connection_id> on stderr. An S3 connection needs --bucket and --region, and an Azure one --account and --container; a missing flag, or one of the other provider's, exits 2 before any request. The request carries an idempotency key, so a retry after a timeout returns the same connection.

FlagWhat it does
--name NAMERequired. The name people see. Up to 200 characters; a blank or longer one exits 2 before any request.
--provider {s3,azure_blob}Required. An S3 bucket, or an Azure Blob container.
--bucket BUCKETS3: your bucket.
--region REGIONS3: the bucket's AWS region, such as us-east-1.
--account ACCOUNTAzure: the storage account name.
--container CONTAINERAzure: your container.
--prefix PREFIXA base prefix inside the bucket or container; leading and trailing / are dropped. Each workspace gets its own folder beneath it. Default: none.

Set up access

nace storage setup --id <connection_id> --aws-account-id 444455556666 --role-name nace-workspaces
# options: s3  access_method=auto  aws_account_id=444455556666  role_name=nace-workspaces
# role arn: arn:aws:iam::444455556666:role/nace-workspaces
# launch url: https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?...
#
# trust policy:
# {
#   "Version": "2012-10-17",
#   ...

nace storage setup prints what to create in your cloud, generated from the connection's saved setup inputs. With no option flags it only reads the setup. With any, it reads the saved inputs, puts the ones you pass over them, and saves them first; flags you leave out keep their saved values. An option flag of the other provider (an S3 one for an Azure connection, or --subscription-id, --resource-group, --tenant-id or --client-id for an S3 one) exits 2 after that read, before anything is saved.

It prints options: and the saved inputs, then for S3 role arn: and launch url: once the account id and role name are saved, then each of trust policy:, permissions policy: and cloudformation template: (S3) or azure cli: (Azure) on a line of its own after a blank line, followed by its text. A policy or template is printed as indented JSON. With --json, it prints the whole setup object.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id.
--access-method {auto,manual}S3: auto for the CloudFormation stack, manual to write the role yourself. Drex's default is auto.
--aws-account-id ACCOUNT_IDS3: the 12-digit AWS account the role is created in.
--role-name NAMES3: the name of the role the stack creates.
--kms-key-arn ARNS3: a KMS key the role may use; the bucket's settings decide encryption.
--subscription-id IDAzure: the subscription the storage account is in.
--resource-group NAMEAzure: the resource group the storage account is in.
--tenant-id IDAzure: your Microsoft Entra tenant.
--client-id IDAzure: the application (client) id to give access to.

Save credentials

nace storage credentials --id <connection_id> --role-arn arn:aws:iam::444455556666:role/nace-workspaces
nace storage credentials --id <connection_id> --tenant-id <tenant_id> --client-id <client_id> --client-secret-file secret.txt
AZURE_CLIENT_SECRET="..." nace storage credentials --id <connection_id> --tenant-id <tenant_id> --client-id <client_id>

nace storage credentials saves the IAM role Nace assumes, or the Azure service principal and its secret, and prints the connection's line, or the whole connection with --json, and saved; next: nace storage validate --id <connection_id> --wait on stderr. They are never returned, and the connection has credentials pending until a validation passes. Pass --role-arn for an S3 connection, or --tenant-id and --client-id for an Azure one; with neither, both kinds, or only one of the Azure ids, it exits 2 before any request.

The Azure secret is never a flag's value, so it stays out of your shell history and process list. It comes from --client-secret-file, a file holding it (one trailing newline is dropped), or - to read stdin, asking at a hidden prompt at a terminal. Without that flag it comes from $AZURE_CLIENT_SECRET. With neither, or an empty secret, the command exits 2 before any request. The CLI never prints the secret, and debug logs show it as ***. Credentials the cloud refuses exit 1 with 422 storage_credentials_refused, whose message never quotes them.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id.
--role-arn ARNS3: your IAM role, arn:aws:iam::<account>:role/<name>, that trusts Nace's roles with the connection's external id.
--tenant-id IDAzure: your Microsoft Entra tenant.
--client-id IDAzure: the application (client) id, with Storage Blob Data Contributor on the container.
--client-secret-file FILEAzure: a file holding the application's secret, or - for stdin. Default: $AZURE_CLIENT_SECRET.

Validate a connection

nace storage validate --id <connection_id> --wait
# succeeded storage_validation <job_id>

nace storage validate starts a storage_validation job and prints its line, <status> storage_validation <job_id>, or the whole job with --json. With --wait, it prints the started job's line on stderr, waits until the job is succeeded, failed or cancelled, and prints that job; a job that didn't succeed exits 1 with error: validation failed: <message> on stderr, the job's error saying what Nace could not do. Still running after --wait-timeout seconds, it exits 1 and the validation keeps running: nace storage get shows validation running until it settles, and ready for a workspace: yes once it passed. A connection Nace revoked can't be validated and fails with 409. The command isn't retried automatically, since a retry after one that went through starts a second validation.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id.
--reactivateRestore a connection you revoked once the validation passes.
--waitWait until the validation finishes, and exit 1 if it failed.
--wait-timeout SECONDSWith --wait, how long to wait (default 180). A negative number exits 2 before any request.

Revoke a connection

nace storage revoke --id <connection_id>
# <connection_id>  revoked  s3://acme-audit-evidence/nace  Audit evidence

nace storage revoke stops Nace using the connection and prints its line, or the whole connection with --json. Workspaces bound to it stop reading and writing until nace storage validate --reactivate passes; their files stay in your bucket.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id.

Delete a connection

nace storage delete --id <connection_id>

nace storage delete deletes a connection no workspace uses and prints deleted <connection_id> on stderr, and nothing on stdout, with or without --json. Nothing in the bucket is touched. A connection a workspace keeps its files in fails with 409. The command isn't retried automatically, since a retry after a delete that went through would fail with 404.

FlagWhat it does
--id CONNECTION_IDRequired. The connection's id.

On this page