Safety
Dynomate classifies each operation as destructive or not. It asks you before it deletes or truncates a table, or uses a request-defined endpoint. It refuses HTTP to remote endpoints.
These rules add to AWS permissions. A request can do only what its AWS profile permits.
Destructive Operations
A destructive operation can remove or replace data, or it runs statements that DNML cannot analyze. Not destructive does not mean read-only. For example, dynamodb.createTable writes, but it cannot remove data.
| Classification | Operations |
|---|---|
| Not destructive | dynamodb.get, dynamodb.query, dynamodb.scan, dynamodb.batchGet, dynamodb.transactGet, dynamodb.describeTable, dynamodb.listTables, dynamodb.createTable, dynamodb.export, athena.listDatabases, athena.listTables, athena.describeTable |
| Destructive | dynamodb.update, dynamodb.delete, dynamodb.batchWrite, dynamodb.transactWrite, dynamodb.deleteTable, dynamodb.truncate, athena.query |
For these operations, the resolved values decide:
| Operation | Destructive if | Not destructive if |
|---|---|---|
dynamodb.put | Any other condition, or no condition. | The condition is exactly attribute_not_exists(<partition key>) for the partition key of the table. |
dynamodb.executeStatement | Other statements. | The first word is SELECT, in any letter case. |
dynamodb.import | conflictMode = "overwrite", the default. | conflictMode = "skip-existing". |
dynamodb.updateTimeToLive | ttlEnabled = true, because DynamoDB then deletes expired items. | ttlEnabled = false. |
This put is not destructive if accountId is the partition key:
[[dynamodb.put]]
name = "Create order"
tableArn = "arn:aws:dynamodb:ap-southeast-2:111122223333:table/Orders"
conditionExpression = "attribute_not_exists(accountId)"
[dynamodb.put.item]
accountId = "account#42"
orderId = "order-1" dynamodb.batchWriteis always destructive, because a put can overwrite an item.athena.queryis always destructive, because Dynomate does not parse SQL. IAM permissions and workgroup settings control what a query can do.- Dynomate classifies an operation after it applies defaults and templates. For a value from an earlier result, this happens before the AWS call.
- Each outcome records if the operation was destructive.
The allowDestructive Key
allowDestructive = true only records that the author knows about the destructive operations. It does not change a classification, a confirmation, an approval or a permission. In the app, it is Acknowledge destructive operations.
Confirmation
The app asks for confirmation, and the CLI asks for approval, one time before a request runs if it contains:
- Table changes: a
dynamodb.deleteTableordynamodb.truncateoperation. - Request-defined endpoints: an
endpointUrlon an operation, in[defaults]or in an import source.
Disabled operations, and operations that depend on them, do not count. Item writes, saved-profile endpoints and the profile or region do not need confirmation or approval.
A confirmation or approval applies only to one run of the request, exactly as prepared. If you change the draft, inputs, environments, file or saved profile settings, Dynomate asks again. Dynomate never saves it.
Confirmation and approval do not skip validation, endpoint rules or guards.
In the app, the Confirm request dialog asks. Select Confirm and run to continue. In the CLI, --approval sets when the CLI asks:
auto, the default: the CLI asks in the cases above.always: the CLI asks for each valid request.never: the CLI does not ask, and the run counts as approved. Automation uses this value.
Guards
dynamodb.deleteTable and dynamodb.truncate accept a guard. Dynomate checks it before it deletes data:
[[dynamodb.truncate]]
name = "Clear development users"
tableArn = "arn:aws:dynamodb:ap-southeast-2:123456789012:table/Users-dev"
guard = { expectTableName = "Users-dev", expectItemCountAtMost = 100000 } expectTableNamemust be the same as the resolved table name.expectItemCountAtMostmust be equal to or more than the item count that DynamoDB reports.
If a guard fails, the operation fails with DNML_GUARD_FAILED and makes no destructive AWS call. Use a guard when the table ARN comes from a variable or an environment.
Endpoints
By default, an operation uses the standard AWS endpoint of its region. A custom endpoint, such as DynamoDB Local, comes from the request or a saved AWS profile.
[defaults]
profileName = "local"
endpointUrl = "http://localhost:8000"
[[dynamodb.scan]]
name = "All orders"
tableArn = "arn:aws:dynamodb:us-east-1:000000000000:table/Orders" Precedence
Dynomate uses the first endpoint that is set:
- The
endpointUrlof the operation. endpointUrlin[defaults].- The endpoint of the selected AWS profile. See Local profiles.
- The standard AWS endpoint.
HTTPS and Loopback
All custom endpoints must obey these rules:
- The URL uses
httporhttpsand has a host name. It has no spaces, credentials or#fragment. - HTTPS works for all host names.
- HTTP works only for
localhost,localhost., an IPv4 loopback address such as127.0.0.1, or::1. Subdomains such asdynamodb.localhostdo not work.
An HTTP endpoint that breaks these rules causes DNML_INSECURE_ENDPOINT. If Dynomate knows the endpoint before the run, no operation runs. If it comes from an earlier result, the operation that uses it fails before it connects.
Request and Saved-Profile Endpoints
- Request-defined endpoints are in the file, and anyone who edits the file can change them. They need confirmation or approval, also if a saved profile has the same value.
- Saved profile endpoints come from your AWS profile settings. They do not need confirmation or approval.
The app shows each endpoint and its source under Effective endpoints. The CLI prints them on each run. Each outcome records the endpoint that the operation used.
Athena Query Ownership
An athena.query continues in AWS after Dynomate submits it. The owner of the query decides what Dynomate does on a timeout or a cancellation:
clientRequestToken | Owner | On timeout or cancellation |
|---|---|---|
| Not set | Dynomate makes a token, and this run owns the query. | After Dynomate knows the query ID, it tries to stop the query. |
| Set in the file | Not known. Another run or tool can use the same token. | Dynomate stops the wait. The query continues in Athena. |
[[athena.query]]
name = "Nightly rollup"
region = "ap-southeast-2"
database = "commerce"
clientRequestToken = "rollup-2026-09-23"
sql = "INSERT INTO daily_totals SELECT ..."
If Dynomate knows the query ID, the failed outcome gives it in error.details.queryExecutionId. Use it to examine or stop the query in the Athena console.
maxRows limits the rows that Dynomate gets, not the data that Athena scans or the cost of the query.