devlift-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. devlift_cli/MANUAL.md +1066 -0
  2. devlift_cli/__init__.py +3 -0
  3. devlift_cli/__main__.py +4 -0
  4. devlift_cli/api/__init__.py +0 -0
  5. devlift_cli/api/approvals.py +53 -0
  6. devlift_cli/api/catalog.py +96 -0
  7. devlift_cli/api/client.py +125 -0
  8. devlift_cli/api/context.py +21 -0
  9. devlift_cli/api/deployments.py +37 -0
  10. devlift_cli/api/infra.py +94 -0
  11. devlift_cli/api/infra_list.py +61 -0
  12. devlift_cli/api/kong.py +29 -0
  13. devlift_cli/api/services.py +106 -0
  14. devlift_cli/api/vpc.py +24 -0
  15. devlift_cli/app.py +163 -0
  16. devlift_cli/auth/__init__.py +0 -0
  17. devlift_cli/auth/oauth.py +270 -0
  18. devlift_cli/auth/session.py +64 -0
  19. devlift_cli/auth/storage.py +135 -0
  20. devlift_cli/commands/__init__.py +0 -0
  21. devlift_cli/commands/approval.py +51 -0
  22. devlift_cli/commands/auth.py +180 -0
  23. devlift_cli/commands/catalog.py +187 -0
  24. devlift_cli/commands/clusters.py +108 -0
  25. devlift_cli/commands/deployment.py +77 -0
  26. devlift_cli/commands/dynamodb.py +121 -0
  27. devlift_cli/commands/eks.py +326 -0
  28. devlift_cli/commands/kong.py +145 -0
  29. devlift_cli/commands/languages.py +40 -0
  30. devlift_cli/commands/manual.py +82 -0
  31. devlift_cli/commands/repositories.py +49 -0
  32. devlift_cli/commands/request.py +89 -0
  33. devlift_cli/commands/s3.py +198 -0
  34. devlift_cli/commands/sqs.py +229 -0
  35. devlift_cli/config.py +94 -0
  36. devlift_cli/context.py +97 -0
  37. devlift_cli/data/placement/vance.json +16 -0
  38. devlift_cli/errors.py +52 -0
  39. devlift_cli/ops/__init__.py +0 -0
  40. devlift_cli/ops/approvals.py +343 -0
  41. devlift_cli/ops/eks.py +877 -0
  42. devlift_cli/ops/kong.py +343 -0
  43. devlift_cli/ops/placement.py +128 -0
  44. devlift_cli/ops/resources.py +418 -0
  45. devlift_cli/ops/status.py +152 -0
  46. devlift_cli/ops/wait.py +82 -0
  47. devlift_cli/render/__init__.py +0 -0
  48. devlift_cli/render/output.py +75 -0
  49. devlift_cli/resolve/__init__.py +0 -0
  50. devlift_cli/resolve/allowlist.py +192 -0
  51. devlift_cli/resolve/names.py +179 -0
  52. devlift_cli-0.1.0.dist-info/METADATA +106 -0
  53. devlift_cli-0.1.0.dist-info/RECORD +56 -0
  54. devlift_cli-0.1.0.dist-info/WHEEL +5 -0
  55. devlift_cli-0.1.0.dist-info/entry_points.txt +3 -0
  56. devlift_cli-0.1.0.dist-info/top_level.txt +1 -0
devlift_cli/MANUAL.md ADDED
@@ -0,0 +1,1066 @@
1
+ # DevLift CLI manual
2
+
3
+ `devlift` (alias `dl`) is the command-line tool for the DevLift platform. It
4
+ works like the AWS CLI: `devlift <group> <operation> [flags]`. Every command
5
+ is a fixed sequence of calls to the DevLift backend's REST API, the same
6
+ endpoints the web dashboard uses. Nothing here talks to an AI model.
7
+
8
+ Read this manual any time with `devlift man` (paged, like `man`: scroll,
9
+ `/` to search, `q` to leave), or print one section with
10
+ `devlift manual <topic>` (for example `devlift manual s3`).
11
+
12
+ ## 1. Install
13
+
14
+ Requires Python 3.11 or newer. Nothing else: the installer puts DevLift in
15
+ its own environment and touches no system packages.
16
+
17
+ ```
18
+ curl -fsSL <install-url>/install.sh | sh
19
+ ```
20
+
21
+ Or, from a wheel you were sent:
22
+
23
+ ```
24
+ sh install.sh ./devlift_cli-<version>-py3-none-any.whl
25
+ ```
26
+
27
+ It installs into `~/.local/share/devlift` and links `devlift` and `dl` into
28
+ `~/.local/bin`, telling you if that is not on your PATH. Re-run it to
29
+ upgrade. To remove it entirely:
30
+
31
+ ```
32
+ rm -rf ~/.local/share/devlift
33
+ rm -f ~/.local/bin/devlift ~/.local/bin/dl
34
+ ```
35
+
36
+ Developers working on the CLI itself clone the repository and run
37
+ `pip install -e .` inside a virtualenv instead.
38
+
39
+ The login token is kept in the OS keyring when one is available, otherwise
40
+ in `~/.config/devlift/credentials.json` with mode 0600.
41
+
42
+ ## 2. First run
43
+
44
+ ```
45
+ devlift configure --base-url http://localhost:8000 --default # where the backend runs
46
+ devlift login # opens the browser
47
+ devlift whoami # who you are, what the server enables
48
+ ```
49
+
50
+ `configure` writes a profile to `~/.config/devlift/cli.json`. A profile is
51
+ just a name, a backend URL and a default output format, and each one keeps
52
+ its own sign-in.
53
+
54
+ ### Switching profiles
55
+
56
+ ```
57
+ devlift profile list what you have; the default is starred
58
+ devlift profile show which one this shell is using, and why
59
+ devlift profile use prod switch, now and for every later command
60
+ ```
61
+
62
+ `profile use` changes the saved default, so it lasts until you change it
63
+ again. For one command only, pass `--profile prod` anywhere on the line. For
64
+ one shell session, export `DEVLIFT_PROFILE=prod`.
65
+
66
+ A new profile is created by configuring a name that does not exist yet:
67
+
68
+ ```
69
+ devlift --profile local configure --base-url http://localhost:8000
70
+ devlift --profile local login
71
+ ```
72
+
73
+ Two things override a switch, and `profile use` warns you when either is set.
74
+ `DEVLIFT_PROFILE` beats the saved default, and `DEVLIFT_BASE_URL` replaces
75
+ the backend of whichever profile is chosen. An exported `DEVLIFT_BASE_URL` is
76
+ the usual reason a switch appears to do nothing, or why a command reports
77
+ being signed in to one backend while targeting another. `devlift profile
78
+ show` names the cause.
79
+
80
+ ## 3. Global options
81
+
82
+ These work anywhere on the command line, before or after the subcommand.
83
+
84
+ | Option | Meaning |
85
+ |---|---|
86
+ | `--profile NAME` | Use this profile (default: the one marked default, or `default`). |
87
+ | `-o, --output table\|json\|yaml` | Output format. Table on a terminal, JSON when piped. |
88
+ | `-y, --yes` | Answer yes to confirmations. |
89
+ | `--no-input` | Never prompt. A missing value is an error (exit 3). Use in scripts and CI. Without `-y`, a write stops at its confirmation (exit 5) after printing its summary on stderr, in `-o json` mode too: a dry run. |
90
+ | `--endpoint-url URL` | Override the backend URL for this one call. |
91
+ | `--debug` | Log every HTTP call to stderr. |
92
+ | `-V, --version` | Print the version. |
93
+ | `-h, --help` | Help for any command. |
94
+
95
+ Environment variables: `DEVLIFT_PROFILE`, `DEVLIFT_BASE_URL` (overrides the
96
+ profile's backend), `DEVLIFT_TOKEN` (use this bearer token instead of the
97
+ stored login), `DEVLIFT_OUTPUT`, `DEVLIFT_NO_CACHE=1` (skip the catalog
98
+ cache), `DEVLIFT_CONFIG_DIR`.
99
+
100
+ Note: a token belongs to the backend it was obtained from. If
101
+ `DEVLIFT_BASE_URL` or `--endpoint-url` points elsewhere the CLI refuses with
102
+ exit 2 and tells you to `devlift login` for that backend.
103
+
104
+ ## 4. Exit codes
105
+
106
+ | Code | Meaning |
107
+ |---|---|
108
+ | 0 | Success |
109
+ | 1 | Error (server error, unexpected failure) |
110
+ | 2 | Not signed in, or the token is for another backend. Run `devlift login`. |
111
+ | 3 | Missing or invalid input, or an ambiguous name |
112
+ | 4 | Permission refused by the server |
113
+ | 5 | Confirmation required (no `--yes` while `--no-input`) |
114
+ | 6 | Not found |
115
+ | 7 | Conflict: name already exists, wrong state, lane busy |
116
+
117
+ Errors go to stderr as `error: <message>` plus an optional hint line. With
118
+ `-o json` stdout carries only JSON, so `devlift … -o json | jq` is safe.
119
+
120
+ ## 5. Placement allowlist
121
+
122
+ The backend lists every region for every application, which is wider than
123
+ what exists. What each product, environment and region really offers is
124
+ recorded per tenant in `devlift_cli/data/placement/{tenant}.json`, shipped
125
+ inside this CLI:
126
+
127
+ ```
128
+ {"<workspace>": {"<product>": {"<env>": {"<aws region>": ["s3", "sqs", ...]}}}}
129
+ ```
130
+
131
+ Every create resolves its placement through that file, so a region the
132
+ config does not list for that service is refused before anything is written:
133
+
134
+ ```
135
+ error: s3 is not offered in 'canada' for core/stage.
136
+ Available there: Mumbai.
137
+ ```
138
+
139
+ `applications list`, `environments list` and `regions list` are filtered by
140
+ it too, so what you are offered is what you can actually create in. They say
141
+ how many rows they hid, and `--all` shows everything the backend reports:
142
+
143
+ ```
144
+ devlift applications list core (falcon has no placement)
145
+ devlift environments list core/stage: Mumbai
146
+ devlift regions list --for s3 Mumbai
147
+ devlift regions list --all Canada, London, Mumbai
148
+ ```
149
+
150
+ Service keys are `s3`, `sqs`, `dynamo`, `eks`, `gateway` and `database`.
151
+ Matching is on names, not codes: product `core`, environment `stage`, region
152
+ `ap-south-1` resolved to its city label. A tenant with no file is not
153
+ filtered at all, and filtering fails closed, so a bad match shows nothing
154
+ rather than a placement that does not exist.
155
+
156
+ The chatbot carries the same file at
157
+ `backend/data/placement/{tenant}.json` and the dashboard a coarser copy.
158
+ Change one, change the others.
159
+
160
+ ## 6. Names instead of codes
161
+
162
+ Wherever a command takes `--app`, `--env`, `--region` or a service, you can
163
+ type the name you see in the dashboard: `--app core`, `--env stage`,
164
+ `--region mumbai`, `services describe java-service`. Matching is
165
+ case-insensitive, exact first, then unique prefix. An ambiguous name is an
166
+ error that lists the candidates. Codes are accepted too.
167
+
168
+ Catalog answers are cached for ten minutes per profile. Add `--no-cache` to a
169
+ list command, or set `DEVLIFT_NO_CACHE=1`, to refetch.
170
+
171
+ ## 7. Commands
172
+
173
+ ### auth
174
+
175
+ ```
176
+ devlift login [--no-browser] Sign in through the browser (OAuth 2.1, PKCE).
177
+ devlift logout Revoke and forget the stored token.
178
+ devlift whoami User, tenant, backend, enabled features.
179
+ devlift configure [--base-url URL] [--default-output FMT] [--default] [--list]
180
+ ```
181
+
182
+ `--no-browser` prints the sign-in URL instead of opening a browser. The
183
+ login waits up to five minutes for the redirect.
184
+
185
+ ### applications, environments, regions, resource-types, resource-groups, repositories, languages
186
+
187
+ ```
188
+ devlift applications list [--active] [--all]
189
+ devlift environments list [--app NAME] [--all] Environments and their regions per application.
190
+ devlift regions list [--app NAME] [--env ENV] Regions you can place resources in (the --region values).
191
+ devlift regions list --for s3 Only the regions that really offer that service.
192
+ devlift regions list --all Skip the placement allowlist (section 5).
193
+ devlift regions list --vendor aws The cloud vendor's raw region list.
194
+ devlift resource-types list [--family FAMILY] Infrastructure types (storage, compute, network, …).
195
+ devlift resource-groups list [--app NAME] Resource groups; services are created inside one.
196
+ ```
197
+
198
+ A resource group is the permission boundary a service lives in, so it
199
+ decides who may later edit, approve and deploy that service. It is asked for
200
+ by `eks create` when the application has more than one.
201
+
202
+ ### services
203
+
204
+ ```
205
+ devlift services list [--app NAME] [--search TEXT]
206
+ devlift services describe <name|code>
207
+ ```
208
+
209
+ ### repositories
210
+
211
+ ```
212
+ devlift repositories list [--search TEXT] Repositories the DevLift GitHub App can see (--repository values).
213
+ devlift repositories branches <owner/name> Branches of one repository (--branch values).
214
+ ```
215
+
216
+ ### languages
217
+
218
+ ```
219
+ devlift languages list Languages and versions a service can use (--language / --version values),
220
+ and whether a starting template exists for each.
221
+ ```
222
+
223
+ ### clusters
224
+
225
+ ```
226
+ devlift clusters list [--app NAME] [--env ENV] [--region REGION] [--type eks|ecs] [--search TEXT] [--all]
227
+ ```
228
+
229
+ The clusters services run on, and the `--cluster` values for `eks create`.
230
+ Narrowed to placements you could really create in: the backend reports only
231
+ the environments this deployment serves, and the placement allowlist
232
+ (section 5) says which of those offer EKS or ECS. Clusters outside both are
233
+ real but unusable from here, so they are hidden and counted; `--all` shows
234
+ everything the backend reports.
235
+
236
+ ```
237
+ devlift clusters list --type eks --app core --env stage
238
+ ```
239
+
240
+ ```
241
+ 22 of 25 hidden: not a placement this deployment offers; --all shows everything.
242
+
243
+ Cluster Type Environment Region Application Registered
244
+ acme-core-stage-mumbai-01-application-cluster EKS stage Mumbai core yes
245
+ …two more in the same placement, not registered
246
+ ```
247
+
248
+ Within that, **Registered** is the flag that matters: only a registered
249
+ cluster may take a new service, and `eks create` will not offer the others.
250
+ A row whose Application reads "tenant-level" belongs to no single product
251
+ and is the fallback for products with no cluster of their own.
252
+
253
+ With exactly one registered cluster in a placement, `eks create` fills it in
254
+ silently. With several it asks, or wants `--cluster` under `--no-input`.
255
+
256
+ ### s3
257
+
258
+ ```
259
+ devlift s3 list [--app NAME] [--env ENV] [--region REGION]
260
+ devlift s3 describe <bucket|code> [--env ENV]
261
+ devlift s3 create --name NAME --app APP --env ENV --region REGION
262
+ [--versioning | --no-versioning]
263
+ [--replication --cross-account-id 123456789012 | --no-replication]
264
+ [--wait] [-y]
265
+ devlift s3 update <bucket|code> [--env ENV] [--app APP]
266
+ [--versioning | --no-versioning]
267
+ [--replication --cross-account-id 123456789012 | --no-replication]
268
+ [--wait] [-y]
269
+ ```
270
+
271
+ Every valid combination of the two settings, for `update` and for `create`,
272
+ is listed further down this section.
273
+
274
+ Section 8 walks a bucket through from the first flag to a finished
275
+ deployment.
276
+
277
+ Bucket names: 3-63 characters, lowercase letters, numbers, dots and hyphens,
278
+ starting and ending with a letter or number. The backend prefixes the tenant
279
+ and region on the real AWS name.
280
+
281
+ `--replication` copies objects to another AWS account and needs
282
+ `--cross-account-id` with that account's 12 digits. Either flag without the
283
+ other is refused (exit 3).
284
+
285
+ Every create also stores the AWS account and cloud region the resource sits
286
+ in, which is what the dashboard canvas uses to place it. They are resolved
287
+ automatically and printed in the summary. If they cannot be resolved the
288
+ command says so before asking for confirmation: the resource would deploy
289
+ normally but never appear on the canvas.
290
+
291
+ `update` changes only the flags you pass. It reads the bucket, shows a
292
+ Field | Current | New table, asks for confirmation, then queues and deploys
293
+ the change exactly as `create` does (section 8). The name, application,
294
+ environment and region cannot change — a bucket somewhere else is a new
295
+ bucket. Passing values the bucket already has is reported and changes
296
+ nothing.
297
+
298
+ **Every combination.** Run each without `-y` first: the diff is printed and
299
+ nothing is written (exit 5). Only the flags you pass change; the rest of the
300
+ bucket is left alone.
301
+
302
+ ```
303
+ # versioning
304
+ devlift s3 update my-logs --versioning # turn it on
305
+ devlift s3 update my-logs --no-versioning # turn it off
306
+
307
+ # replication
308
+ devlift s3 update my-logs --replication --cross-account-id 123456789012
309
+ devlift s3 update my-logs --cross-account-id 210987654321 # keep it on, change the target
310
+ devlift s3 update my-logs --replication # only if a target is already stored
311
+ devlift s3 update my-logs --no-replication # off, and the target is cleared
312
+
313
+ # both at once: one diff, one confirmation, one deployment
314
+ devlift s3 update my-logs --no-versioning --replication --cross-account-id 123456789012
315
+ devlift s3 update my-logs --versioning --no-replication
316
+
317
+ # doing it for real, and following the deployment
318
+ devlift s3 update my-logs --no-versioning --replication --cross-account-id 123456789012 -y
319
+ devlift s3 update my-logs --versioning -y --wait
320
+ devlift s3 update my-logs --no-replication -y -o json
321
+ ```
322
+
323
+ What each combination does:
324
+
325
+ | Flags | Result |
326
+ |---|---|
327
+ | `--versioning` / `--no-versioning` | Versioning on or off. Nothing else touched. |
328
+ | `--replication --cross-account-id ID` | Replication on, replicating to that account. |
329
+ | `--cross-account-id ID` alone | Changes the target account, leaving replication as it is. |
330
+ | `--replication` alone | Only works when the bucket already stores a target account. Without one: "Replication needs a target account" (exit 3). |
331
+ | `--no-replication` | Replication off and the target account emptied, both shown in the diff. |
332
+ | `--no-replication --cross-account-id ID` | Refused (exit 3). An account id means nothing with replication off. |
333
+ | Values the bucket already has | "already has those values. Nothing to change", exit 0, `changed: false`. |
334
+ | No flags at all | Refused (exit 3), listing the flags it accepts. |
335
+
336
+ The same pairs apply to `create`, with one difference: there both
337
+ decisions must be stated. On a terminal the command asks for anything you
338
+ leave out; under `--no-input` a missing one is an error.
339
+
340
+ ```
341
+ devlift s3 create --name my-logs --app core --env stage --region mumbai --versioning --no-replication
342
+ devlift s3 create --name my-logs --app core --env stage --region mumbai --no-versioning --no-replication
343
+ devlift s3 create --name my-logs --app core --env stage --region mumbai --versioning --replication --cross-account-id 123456789012
344
+ devlift s3 create --name my-logs --app core --env stage --region mumbai --no-versioning --replication --cross-account-id 123456789012
345
+ ```
346
+
347
+ `--app` and `--env` are normally unnecessary. `--env` only disambiguates a
348
+ name that exists in several environments. `--app` is only needed when the
349
+ backend does not report which application a resource belongs to and more
350
+ than one could own it; otherwise the command infers it from the placement
351
+ allowlist and says which it used.
352
+
353
+ An update also repairs a resource that is missing the dashboard placement:
354
+ if the stored record has no AWS account and cloud region, they are resolved
355
+ and added, and the command says so. That is the fix for a resource created
356
+ by an earlier version of this CLI that never appears on the canvas.
357
+
358
+ ### sqs
359
+
360
+ ```
361
+ devlift sqs list [--app NAME] [--env ENV] [--region REGION]
362
+ devlift sqs describe <queue|code> [--env ENV]
363
+ devlift sqs create --name NAME --app APP --env ENV --region REGION
364
+ [--fifo | --no-fifo] [--dlq | --no-dlq]
365
+ [--cross-account-id ID ...]
366
+ [--max-receive-count 1-1000] [--visibility-timeout 0-43200]
367
+ [--retention 60-1209600] [--dlq-retention 60-1209600]
368
+ [--wait] [-y]
369
+ devlift sqs update <queue|code> [--env ENV] [--app APP]
370
+ [--dlq | --no-dlq]
371
+ [--cross-account-id ID ... | --clear-cross-accounts]
372
+ [--max-receive-count 1-1000] [--visibility-timeout 0-43200]
373
+ [--retention 60-1209600] [--dlq-retention 60-1209600]
374
+ [--wait] [-y]
375
+ ```
376
+
377
+ Queue names: letters, numbers, hyphens and underscores, at most 80
378
+ characters, without the `.fifo` suffix (added automatically for FIFO queues).
379
+
380
+ **Creating a queue.** Two decisions must be stated, FIFO and dead-letter
381
+ queue. On a terminal the command asks for whichever you leave out; under
382
+ `--no-input` a missing one is an error. Everything else is optional and
383
+ falls back to the platform's defaults.
384
+
385
+ ```
386
+ # the four basic shapes
387
+ devlift sqs create --name orders --app core --env stage --region mumbai --no-fifo --no-dlq
388
+ devlift sqs create --name orders --app core --env stage --region mumbai --no-fifo --dlq
389
+ devlift sqs create --name orders --app core --env stage --region mumbai --fifo --no-dlq
390
+ devlift sqs create --name orders --app core --env stage --region mumbai --fifo --dlq
391
+
392
+ # a dead-letter queue with its retry budget and its own retention
393
+ devlift sqs create --name orders --app core --env stage --region mumbai \
394
+ --no-fifo --dlq --max-receive-count 5 --dlq-retention 1209600
395
+
396
+ # message timings
397
+ devlift sqs create --name orders --app core --env stage --region mumbai \
398
+ --no-fifo --no-dlq --visibility-timeout 60 --retention 345600
399
+
400
+ # other AWS accounts allowed to use the queue; repeat the flag per account
401
+ devlift sqs create --name orders --app core --env stage --region mumbai \
402
+ --no-fifo --no-dlq --cross-account-id 123456789012 --cross-account-id 210987654321
403
+
404
+ # everything at once, created for real and followed to the end
405
+ devlift sqs create --name orders --app core --env stage --region mumbai \
406
+ --no-fifo --dlq --max-receive-count 5 \
407
+ --visibility-timeout 60 --retention 345600 --dlq-retention 1209600 \
408
+ --cross-account-id 123456789012 -y --wait
409
+ ```
410
+
411
+ | Flag | Meaning | Accepted values |
412
+ |---|---|---|
413
+ | `--fifo` / `--no-fifo` | First-in-first-out ordering. Part of the real queue name, so it can never be changed later. | required |
414
+ | `--dlq` / `--no-dlq` | Create a dead-letter queue beside this one. | required |
415
+ | `--max-receive-count` | Receives before a message is moved to the dead-letter queue. | 1 to 1000 |
416
+ | `--visibility-timeout` | Seconds a received message stays hidden from other consumers. | 0 to 43200 |
417
+ | `--retention` | Seconds a message is kept. | 60 to 1209600 |
418
+ | `--dlq-retention` | The same, for the dead-letter queue. | 60 to 1209600 |
419
+ | `--cross-account-id` | A 12-digit AWS account allowed to use the queue. Repeat it for several. | repeatable |
420
+
421
+ Refusals, all before anything is written (exit 3): a number outside its
422
+ range, an account id that is not 12 digits, a name ending in `.fifo`, and a
423
+ missing `--fifo` or `--dlq` under `--no-input`. Note that
424
+ `--max-receive-count` is accepted with `--no-dlq`, but it has nothing to act
425
+ on, since the count is what moves a message to a dead-letter queue.
426
+
427
+ **Changing a queue.** `update` works like `s3 update`: only the flags you
428
+ pass change, the diff is shown and confirmed, then the change is queued and
429
+ deployed. The name, the FIFO flag, application, environment and region
430
+ cannot change, since FIFO is part of the queue's real name.
431
+
432
+ ```
433
+ devlift sqs update orders --dlq --max-receive-count 5 # add a dead-letter queue
434
+ devlift sqs update orders --no-dlq # remove it
435
+ devlift sqs update orders --visibility-timeout 120 # one timing
436
+ devlift sqs update orders --retention 604800 --dlq-retention 604800
437
+ devlift sqs update orders --cross-account-id 123456789012 # REPLACES the whole list
438
+ devlift sqs update orders --clear-cross-accounts # remove every grant
439
+ devlift sqs update orders --dlq --max-receive-count 5 -y --wait
440
+ ```
441
+
442
+ `--cross-account-id` on an update replaces the whole list rather than adding
443
+ to it, so pass every account you want to keep. `--clear-cross-accounts`
444
+ empties it, and the two cannot be combined.
445
+
446
+ ### dynamodb
447
+
448
+ ```
449
+ devlift dynamodb list [--app NAME] [--env ENV] [--region REGION]
450
+ devlift dynamodb describe <table|code> [--env ENV]
451
+ devlift dynamodb create --name NAME --app APP --env ENV --region REGION
452
+ --partition-key ATTR [--partition-key-type S|N|B]
453
+ [--wait] [-y]
454
+ ```
455
+
456
+ Table names: 3-200 characters of letters, numbers, dots, underscores and
457
+ hyphens.
458
+
459
+ **Creating a table.** Only the name and the partition key are yours to
460
+ decide; the key type defaults to string. These are the same three fields the
461
+ dashboard and the assistant ask for.
462
+
463
+ ```
464
+ # the usual case: a string partition key
465
+ devlift dynamodb create --name orders --partition-key order_id
466
+
467
+ # a numeric or binary key
468
+ devlift dynamodb create --name orders --partition-key created_at --partition-key-type N
469
+ devlift dynamodb create --name orders --partition-key payload_id --partition-key-type B
470
+
471
+ # spelled out in full, for a script
472
+ devlift dynamodb create --name orders --app core --env stage --region mumbai \
473
+ --partition-key order_id --partition-key-type S
474
+
475
+ # created for real and followed to the end
476
+ devlift dynamodb create --name orders --partition-key order_id -y --wait
477
+ ```
478
+
479
+ | Flag | Meaning | Accepted values |
480
+ |---|---|---|
481
+ | `--name` | Table name. | 3 to 200 characters, letters, numbers, dots, underscores, hyphens |
482
+ | `--partition-key` | The attribute that partitions the table. | 1 to 255 characters, letters, numbers, dots, underscores, hyphens |
483
+ | `--partition-key-type` | Its type. | `S` string (default), `N` number, `B` binary |
484
+
485
+ Refusals, all before anything is written (exit 3): a name shorter than
486
+ three characters or with other punctuation, a partition key with invalid
487
+ characters, and a key type other than S, N or B. On a terminal the name and
488
+ partition key are asked for when omitted; under `--no-input` they are
489
+ errors.
490
+
491
+ **There is no `dynamodb update`.** A table's partition key is fixed at
492
+ creation and cannot be changed afterwards, so the command does not exist.
493
+ Trying it exits 2 with "No such command". A different key means a different
494
+ table.
495
+
496
+ ### eks
497
+
498
+ ```
499
+ devlift eks create --name NAME --app APP --env ENV --region REGION --type api|worker
500
+ [--resource-group GROUP] [--cluster CLUSTER]
501
+ --repository owner/name --branch main [--branch ...]
502
+ --language Go|Python|Node.js|"Java Maven"|"Java Gradle" --version 1.24
503
+ [--set key=value ...] [--public] [-y]
504
+ devlift eks settings Every key --set accepts, and when each applies.
505
+ devlift eks settings <service> Only the keys that apply to it, with its current values.
506
+ devlift eks show <service> --env ENV The configuration as it stands, with pending values marked.
507
+ devlift eks diff <service> --env ENV Preview the pending change: Field | Deployed | Requested.
508
+ devlift eks diff <service> --deployed The live configuration versus the last deployment.
509
+ devlift eks edit <service> --env ENV [--set key=value ...]
510
+ [--repository owner/name] [--branch ...] [--language L --version V] [-y]
511
+ ```
512
+
513
+ **Gather the answers first.** Placement resolves itself, but a service needs
514
+ a resource group, a repository, a branch on it and a language version. Each
515
+ has a command that lists the accepted values, so none of them has to be
516
+ guessed:
517
+
518
+ ```
519
+ # 1. the resource group that will own the service
520
+ devlift resource-groups list --app core
521
+
522
+ # 2. the repository the build reads, and a branch on it
523
+ devlift repositories list --search admin
524
+ devlift repositories branches example-org/admin-api-server
525
+
526
+ # 3. the language and version
527
+ devlift languages list
528
+
529
+ # 4. the cluster, when the placement has more than one registered
530
+ devlift clusters list --type eks --app core --env stage
531
+
532
+ # 5. the settings --set accepts, and when each applies
533
+ devlift eks settings # all of them
534
+ devlift eks settings --language go --type api # only the ones this service would use
535
+ ```
536
+
537
+ The resource group is worth a moment: it is part of the service's
538
+ permission path, so it decides who can later edit, approve and deploy it.
539
+ Pick the team that owns the service rather than the default group. The
540
+ repository must be one the DevLift GitHub App can see, and the branch must
541
+ exist on it; both are checked before anything is written.
542
+
543
+ **Then create it.** Run it once without `-y` to see the two tables, then
544
+ again with `-y`:
545
+
546
+ ```
547
+ devlift eks create --name orders \
548
+ --resource-group "Default service group" --type api \
549
+ --repo example-org/admin-api-server --branch main \
550
+ --language go --version 1.24
551
+
552
+ devlift eks create --name orders \
553
+ --resource-group "Default service group" --type api \
554
+ --repo example-org/admin-api-server --branch main \
555
+ --language go --version 1.24 -y
556
+ ```
557
+
558
+ Add `--app`, `--env` and `--region` to pin the placement, `--cluster` when
559
+ more than one is available, and `--set key=value` for any setting you want
560
+ to differ from the language template.
561
+
562
+ **Add Kong routes, if the service needs them.** They join the same change
563
+ set, so both halves are reviewed and deployed together:
564
+
565
+ ```
566
+ devlift kong routes list orders # what it has now
567
+ devlift kong route add orders --method GET --auth jwt --path '~/orders-service/health$'
568
+ devlift kong route add orders --method GET --auth jwt --path '~/orders-service/health$' -y
569
+ ```
570
+
571
+ **Preview what a reviewer will see:**
572
+
573
+ ```
574
+ devlift eks diff orders --env stage # one table per request, Deployed | Requested
575
+ ```
576
+
577
+ **Then take it through review and deploy:**
578
+
579
+ ```
580
+ devlift request submit orders # freezes the diff, notifies approvers
581
+ devlift approval approve orders # needs can_approve
582
+ devlift eks deploy orders --env stage # prints the same preview, then asks
583
+ devlift eks deploy orders --env stage -y --wait
584
+ ```
585
+
586
+ **Then watch it land.** The deploy prints a workflow id; both of these read
587
+ it back for you:
588
+
589
+ ```
590
+ devlift eks status orders --env stage # once
591
+ devlift eks status orders --env stage --wait # follow it to the end
592
+ ```
593
+
594
+ `eks status` answers the two separate questions in one go: whether DevLift's
595
+ pipeline finished, with its stages and pull request, and whether ArgoCD
596
+ reports the pods actually serving. They are not the same moment. The
597
+ pipeline turns green when the manifests are merged, and on a first deploy
598
+ the image is still being built by GitHub Actions outside DevLift, so ArgoCD
599
+ can report "waiting" for a while afterwards.
600
+
601
+ By workflow id instead, or when you have lost it:
602
+
603
+ ```
604
+ devlift deployment status <workflow-id> --wait
605
+ devlift deployment list --status running
606
+ devlift deployment list --limit 10
607
+ ```
608
+
609
+ The service name becomes the Kubernetes namespace and the ECR repository,
610
+ and cannot be renamed once deployed, so choose it before the first create.
611
+
612
+ `eks create` creates a service and saves its first configuration as a **draft** for
613
+ review. It is the dashboard's "Create & Add" followed by the Settings tab's
614
+ Save, in one command:
615
+
616
+ 1. Register the service (`POST /services/create-service`): name, type,
617
+ application, resource group.
618
+ 2. Create its configuration row for that environment and region
619
+ (`POST /service-configs`) on the chosen EKS cluster. Only the placement
620
+ goes here: cluster, ALB selection (`api` uses the shared ALB, `worker`
621
+ has none) and namespace.
622
+ 3. Park the settings as a DRAFT queue item
623
+ (`POST /transaction/service-settings/<config>`), case `update_service`.
624
+
625
+ Nothing is deployed. The draft goes through the review lane: `request
626
+ submit`, `approval approve`, `eks deploy` (section "request, approval,
627
+ eks deploy"), or the dashboard's Preview tab.
628
+
629
+ **Values.** You give what no template can know: repository, branches,
630
+ language and version. The repository must be one the DevLift GitHub App can
631
+ see (`devlift repositories list`; a bare name is accepted when unique) and
632
+ each branch must exist on it (`devlift repositories branches owner/name`).
633
+ `--branch` is required: on a terminal the branches are listed and the
634
+ repository's default branch is offered as the default answer; under
635
+ `--no-input` a missing branch is an error (exit 3). `devlift languages list`
636
+ shows the languages and versions. Every other setting (port, health and service paths,
637
+ CPU and memory, replicas or autoscaling, Dockerfile options, IAM and the
638
+ ECR/secrets/SSM/Argo flags) starts from the platform's template for that
639
+ language, the same one the assistant applies. The full table is printed with
640
+ a "From" column before you confirm. `--set key=value` overrides any of them;
641
+ `devlift eks settings` lists the keys. Booleans are `true`/`false`, lists are
642
+ comma-separated (`--set custom_iam_policies=s3,sqs`), build args are
643
+ `NAME=VALUE` pairs (`--set build_args=APP_ENV=prod,REGION=ap-south-1`).
644
+ Where the template has no reliable default, the command asks on a terminal,
645
+ or exits 3 naming the `--set` key under `--no-input`.
646
+
647
+ **Finding the `--set` keys.** `devlift eks settings` is the list, and naming
648
+ a service narrows it to that service, adding the value each key holds today.
649
+ That is the list to work from before an edit: what you can change, what it
650
+ is now, and what values are accepted.
651
+
652
+ ```
653
+ devlift eks settings orders --env stage
654
+
655
+ orders (stage)
656
+ Key Setting Values Current Required
657
+ port Port text 8080 yes
658
+ alb_schema ALB schema internal | internet-facing internal yes
659
+ cpu_limit CPU limit (cores) text 0.25 yes
660
+ go_config_path Go config path text configs/…
661
+ ```
662
+
663
+ `--language go --type api` narrows the same way without naming a service,
664
+ which is what to use before a create. An unknown key is refused by `--set`
665
+ with the full list, so a typo never reaches the backend.
666
+
667
+ **Passing several settings.** Repeat the flag, or put them in one `--set`
668
+ separated by semicolons. Both produce one diff, one confirmation and one
669
+ deployment:
670
+
671
+ ```
672
+ --set cpu_limit=0.5 --set memory_limit=0.5 --set port=9090
673
+ --set "cpu_limit=0.5;memory_limit=0.5;port=9090"
674
+ ```
675
+
676
+ The separator is a semicolon, not a comma, because a comma already
677
+ separates the values inside one setting. The two nest cleanly:
678
+
679
+ ```
680
+ --set "custom_iam_policies=s3,sqs;cpu_limit=0.5" # one setting with two values, and another setting
681
+ --set "build_args=APP_ENV=prod,REGION=ap-south-1;port=9090"
682
+ ```
683
+
684
+ The semicolon form must be quoted, because a bare `;` ends the command in
685
+ the shell. Repeated flags need no quotes, so use those if you would rather
686
+ not quote. Commas need no quoting either.
687
+
688
+ ```
689
+ --set "cpu_limit=0.5;port=9090" # quoted: the shell would split it otherwise
690
+ --set cpu_limit=0.5 --set port=9090 # no quotes needed
691
+ --set custom_iam_policies=s3,sqs # no quotes needed
692
+ ```
693
+
694
+ **Per-language and conditional settings.** Not every key applies to every
695
+ service; the rules are the platform form's own: `xms` / `xmx` only for Java,
696
+ `go_config_path` / `go_use_aws_secrets` only for Go, `build_path` for Java
697
+ and Go, `dockerfile_path` only with `generate_dockerfile=false`,
698
+ `alb_schema` / `health` / `service_path` only for `--type api`,
699
+ `min_replicas` / `max_replicas` only with `hpa_enabled=true` and
700
+ `replica_count` only without it. `devlift eks settings` shows the table.
701
+ A template value that does not apply falls away; a `--set` that does not
702
+ apply is refused with the reason (exit 3). Values are checked the way the
703
+ form checks them: CPU 0.1-4 cores and memory 0.1-8 GiB with limit >= request,
704
+ replicas 1-100 with max > min, paths starting with `/`, heap sizes like
705
+ `512m`, IAM policies from s3 / sqs / dynamodb / ses, build-arg names in
706
+ uppercase. Service names are lowercase letters, numbers and hyphens.
707
+
708
+ **Cluster.** The registered EKS cluster for that product, environment and
709
+ region is picked automatically; tenant-level clusters are the fallback.
710
+ Several candidates are asked for, or named with `--cluster`.
711
+
712
+ A name that already exists in the application is refused (exit 7); adding a
713
+ configuration in another environment for an existing service is not
714
+ available yet.
715
+
716
+ **Editing.** `eks edit` is the Settings tab's Save from the terminal. It
717
+ starts from the configuration as it stands — the live values with your open
718
+ draft, if any, laid over them — applies your changes, prints a Setting /
719
+ Current / New table of only what changes, and saves the whole result as a
720
+ settings draft (`POST /transaction/service-settings/<config>`). An open
721
+ draft of yours is updated in place (same queue code), so repeated edits stay
722
+ one request. A submitted or approved request on the service, whoever's,
723
+ blocks editing (exit 7) until it is deployed, withdrawn or revoked: its
724
+ values are being decided on. Passing values the service already has changes
725
+ nothing. The same per-language and conditional rules as `eks create` apply.
726
+ **Previewing a change.** `eks diff` is the preview: the pending request's
727
+ own table of Field, Deployed and Requested, computed by DevLift against the
728
+ live configuration. It is the same table the dashboard's Preview tab and the
729
+ assistant render, so all three agree on what a reviewer will decide on. One
730
+ table per request, since a service's settings and its gateway routes are two
731
+ rows of one change.
732
+
733
+ ```
734
+ devlift eks diff orders --env stage
735
+
736
+ orders (stage / Mumbai) · Settings · draft (queue-…, by a. developer)
737
+ Field Deployed Requested
738
+ port – 8080
739
+ health – /orders-service/health
740
+ cpu_limit – 0.25m
741
+ ```
742
+
743
+ With nothing pending it says so. `--deployed` answers a different question,
744
+ the redeploy drift: what the **live** configuration holds versus what was
745
+ last deployed. That one ignores drafts entirely, so on a service whose
746
+ change is still a draft it shows the live row and its defaults, not what the
747
+ draft proposes.
748
+
749
+ `eks show` marks each value that a pending request would change.
750
+
751
+ ### kong
752
+
753
+ ```
754
+ devlift kong routes list <service> [--env ENV] [--region REGION]
755
+ devlift kong route add <service> --env ENV --method GET --auth jwt|public
756
+ --path '~/api/v1/orders$' [--path ...]
757
+ [--tag TAG] [--priority 0-10000] [--plugin "User ID Injection"] [-y]
758
+ devlift kong route remove <service> --env ENV --method GET --tag TAG --path '~/...$' [--path ...] [-y]
759
+ devlift kong route rename <service> --env ENV --method GET --tag TAG --from '~/old$' --to '~/new$' [-y]
760
+ devlift kong plugin add <service> --env ENV --method GET --tag TAG --plugin "User ID Injection" [--priority N] [-y]
761
+ ```
762
+
763
+ The gateway of one service configuration (environment + region), read and
764
+ written the way the dashboard's Gateway tab does it. `routes list` shows
765
+ the route groups: method, auth, tag, paths (with "not deployed" for paths
766
+ that are still pending), regex priority, extra plugins and any pending
767
+ queue items on the group.
768
+
769
+ A change targets **one route group**: a tag for one HTTP method. `route add`
770
+ creates the group when it does not exist, named after the service (`orders`
771
+ for JWT, `orders-open` for public) unless `--tag` says otherwise. A tag is
772
+ one group per method and its auth is fixed: adding a public path to a JWT
773
+ tag is refused (exit 7), pick another tag. Paths already on the group are
774
+ reported and skipped; a path to remove or rename that is not on the group is
775
+ an error listing what is (exit 6). Nothing to change exits 0 with
776
+ `changed: false`.
777
+
778
+ Paths are Kong regex paths: start with `~/`, end with `$`, no spaces,
779
+ parameters as regex groups (`~/api/v1/users/(?<id>[^/]+)$`, never `/:id`).
780
+ Tags are letters, numbers, underscores and single hyphens. JWT comes from
781
+ `--auth`; `--plugin` is for the extra plugins the tab offers.
782
+
783
+ The change is printed and confirmed, then saved as a DRAFT queue item
784
+ (`POST /transaction/kong-gateway/<config>`, the gateway half of the
785
+ service's change set). Nothing reaches Kong until the draft is submitted,
786
+ approved and deployed (`request submit`, `approval approve`, `eks deploy`).
787
+
788
+ ### request, approval, eks deploy — the review lane
789
+
790
+ ```
791
+ devlift request list [--status draft|submit|approved|rejected] [--service NAME --env ENV] [--mine]
792
+ devlift request show <queue-code|service> [--env ENV]
793
+ devlift request history <service> [--env ENV]
794
+ devlift request submit <queue-code|service> [--env ENV] [--comment TEXT] [-y]
795
+ devlift request withdraw <queue-code|service> [--env ENV] [--comment TEXT] [-y]
796
+ devlift request discard <queue-code|service> [--env ENV] [-y]
797
+
798
+ devlift approval list
799
+ devlift approval approve <queue-code|service> [--env ENV] [--comment TEXT] [-y]
800
+ devlift approval request-changes <queue-code|service> [--env ENV] --comment TEXT [-y]
801
+ devlift approval reject <queue-code|service> [--env ENV] --comment TEXT [-y]
802
+ devlift approval revoke <queue-code|service> [--env ENV] [--comment TEXT] [-y]
803
+
804
+ devlift eks deploy <service> --env ENV [--region REGION] [--confirm-name NAME] [--wait] [-y]
805
+ ```
806
+
807
+ A service's change is one **change set**: its settings request, its gateway
808
+ routes request and its variables request, for one configuration and one
809
+ author. Every verb moves the whole set, so naming a service acts on all of
810
+ its rows, and the output lists each one. A queue code names its own set.
811
+
812
+ ```
813
+ draft ──submit──▶ submit ──approve──▶ approved ──deploy──▶ in flight
814
+ ▲ │ ▲ │
815
+ └────withdraw─────┘ └──────revoke───────┘
816
+ └──request-changes──┘ reject ──▶ rejected (terminal)
817
+ discard throws a draft away
818
+ ```
819
+
820
+ - **submit** (author): the diff is frozen per row; a reviewer decides on
821
+ exactly that. One live request per service at a time: a second author is
822
+ told whose request holds the lane (exit 7).
823
+ - **withdraw** (submitter only) and **request-changes** (reviewer, comment
824
+ required) both return the set to draft. **reject** (reviewer, comment
825
+ required) is terminal.
826
+ - **approve** (needs `can_approve`; self-approval only where the resource
827
+ group's rule allows it) seals each row. **revoke** takes an approval back
828
+ while nothing has started deploying; the request waits for a decision
829
+ again.
830
+ - **eks deploy** prints what is about to ship before it asks: the approved
831
+ requests, then each one's Field | Deployed | Requested table, the same
832
+ preview `eks diff` and the dashboard show. It ships the set with one call
833
+ (`POST /deployments/multiple-deploy {service_config_code}`): the backend
834
+ resolves the approved rows, re-checks the seal, and starts one workflow.
835
+ On **prod** the service name must be typed back (`--confirm-name` when
836
+ not on a terminal); `-y` alone is not enough. `--wait` follows the
837
+ orchestrator (variables, then the service) to completion.
838
+
839
+ Each write prints the request(s) with the frozen diff, then confirms. `-o
840
+ json` returns the rows and their new status. `request show` on a draft has
841
+ no diff yet; the dashboard's Preview tab computes one live.
842
+
843
+ ### deployment, queue, eks status
844
+
845
+ ```
846
+ devlift deployment list [--status running|completed|failed|timedout] [--app NAME] [--env ENV] [--mine] [--limit N]
847
+ devlift deployment status <workflow-id> [--wait] One deployment: status, PR, resources, pipeline stages.
848
+ devlift eks status <service> --env ENV [--wait] [--no-argo]
849
+ devlift queue list [--env ENV] Your transaction-queue items.
850
+ ```
851
+
852
+ `deployment list` and `deployment status` read the same history the
853
+ dashboard's deployment tracker shows: every workflow (resource creates,
854
+ service deploys), who started it, its pull request, the resources it
855
+ carried and the pipeline stages with their timings. `--wait` polls until
856
+ the workflow reaches a terminal status.
857
+
858
+ `eks status` answers two different questions for one service configuration:
859
+
860
+ 1. **Deployment** — did DevLift's pipeline finish? The latest workflow that
861
+ carried this configuration, with its stages and PR.
862
+ 2. **Application** — is the service actually serving? Asked of ArgoCD
863
+ (`GET /deployments/application-status`, the same implementation the
864
+ assistant uses). DevLift's pipeline turns green when the manifests are
865
+ merged, before ArgoCD rolls a pod, so the ArgoCD reading is compared
866
+ against the deploy's finish time: activity older than the deploy means
867
+ "waiting for ArgoCD", not "up". `state` is one of up, starting,
868
+ unhealthy, waiting_for_argocd, not_picked_up, missing, suspended,
869
+ not_found, not_configured (no ArgoCD for that environment), unavailable.
870
+ Health, sync, the running image, and the ArgoCD and health-check URLs
871
+ are shown when known. Note that DevLift never learns the image tag the
872
+ app repository built, so "up" means a healthy pod, not proof that the
873
+ new code is live.
874
+
875
+ `--wait` follows the deployment to its end and then ArgoCD until the state
876
+ settles (up to ten minutes). `--no-argo` skips the ArgoCD check.
877
+
878
+ ### manual
879
+
880
+ ```
881
+ devlift man This whole document, paged like `man` (scroll, / to search, q to leave).
882
+ devlift manual [topic] The same; with a topic, one section printed straight through (`devlift manual s3`).
883
+ devlift manual --no-pager The whole document without paging. --raw prints plain Markdown.
884
+ devlift <group> manual This group's section, from inside the group (`devlift sqs manual`).
885
+ ```
886
+
887
+ Every command group carries its own `manual`: `s3`, `sqs`, `dynamodb`,
888
+ `eks`, `kong`, `request`, `approval`, `deployment`, `repositories` and
889
+ `languages`. It prints exactly what `devlift manual <group>` prints, so it
890
+ can be read without leaving the group you were typing in. `--raw` gives
891
+ plain Markdown.
892
+
893
+ ## 8. Creating a bucket, start to finish
894
+
895
+ The same five steps work for a queue or a table: swap `s3 create` for
896
+ `sqs create` or `dynamodb create`.
897
+
898
+ **1. Find the values for the flags.** Names are accepted everywhere, so this
899
+ is only needed the first time.
900
+
901
+ ```
902
+ devlift applications list # --app
903
+ devlift environments list --app core # --env, and the regions per environment
904
+ devlift regions list --app core --env stage # --region
905
+ ```
906
+
907
+ All three are already filtered by the placement allowlist (section 5), so
908
+ anything they list is somewhere a bucket can really go.
909
+
910
+ **2. Choose the bucket's settings.** Two decisions, and both must be stated.
911
+
912
+ ```
913
+ --versioning | --no-versioning keep old object versions
914
+ --replication --cross-account-id 123456789012 copy objects to another AWS account
915
+ --no-replication no copy
916
+ ```
917
+
918
+ Replication needs a target account, so `--replication` without
919
+ `--cross-account-id` is refused (exit 3), and `--cross-account-id` without
920
+ `--replication` is refused too. The account id is the 12-digit AWS account
921
+ that receives the copies, not the one the bucket lives in.
922
+
923
+ **3. Dry run it.** Leave `-y` off. The command resolves everything, checks
924
+ the name is free, prints the summary and stops before writing anything
925
+ (exit 5).
926
+
927
+ ```
928
+ devlift s3 create --name my-logs --app core --env stage --region mumbai \
929
+ --versioning --no-replication
930
+ ```
931
+
932
+ With replication instead:
933
+
934
+ ```
935
+ devlift s3 create --name my-logs --app core --env stage --region mumbai \
936
+ --versioning --replication --cross-account-id 123456789012
937
+ ```
938
+
939
+ ```
940
+ Create bucket
941
+ Bucket my-logs
942
+ Application core
943
+ Environment stage
944
+ Region Mumbai
945
+ AWS account 111122223333
946
+ Cloud region ap-south-1
947
+ Versioning yes
948
+ Replication yes
949
+ Replication account 123456789012
950
+ ```
951
+
952
+ Check the AWS account and cloud region lines. They are what places the
953
+ bucket on the dashboard canvas. If they are missing the command warns you,
954
+ and the bucket will deploy but stay invisible on the canvas.
955
+
956
+ **4. Create it for real.** Add `-y` to the same command. This registers the
957
+ bucket, queues it, approves it and starts the deployment.
958
+
959
+ ```
960
+ devlift s3 create --name my-logs --app core --env stage --region mumbai \
961
+ --versioning --replication --cross-account-id 123456789012 -y
962
+ ```
963
+
964
+ ```
965
+ Registered bucket my-logs (INFRA_S3_1A2B3C4D).
966
+ Deployment queued (workflow deploy-example-1a2b3c4d5e6f).
967
+
968
+ Bucket my-logs
969
+ Resource code INFRA_S3_1A2B3C4D
970
+ Queue item queue-5e6f7a8b9c0d
971
+ Workflow deploy-example-1a2b3c4d5e6f
972
+ ```
973
+
974
+ Keep the workflow id. Adding `--wait` to the create does the next step for
975
+ you.
976
+
977
+ **5. Watch the deployment.** The backend opens a pull request and applies it.
978
+
979
+ ```
980
+ devlift deployment status deploy-example-1a2b3c4d5e6f --wait
981
+ ```
982
+
983
+ Without `--wait` it prints the current state once. With it, the command
984
+ follows the workflow and returns when it is done or failed. The output
985
+ names the status, the pull request, the resources in the deployment and
986
+ each pipeline stage with its timing.
987
+
988
+ Lost the id? The history is searchable:
989
+
990
+ ```
991
+ devlift deployment list --limit 10
992
+ devlift deployment list --status running
993
+ ```
994
+
995
+ **6. Confirm the bucket exists.**
996
+
997
+ ```
998
+ devlift s3 describe my-logs --env stage
999
+ devlift s3 list --app core --env stage --region mumbai
1000
+ ```
1001
+
1002
+ `describe` shows the real AWS name, which carries a tenant and region
1003
+ prefix, so `my-logs` becomes something like
1004
+ `acme-core-stage-mumbai-01-my-logs`. On the dashboard canvas, search for
1005
+ the name you passed to `--name`.
1006
+
1007
+ ### What a create does underneath
1008
+
1009
+ 1. Resolve `--app`, `--env`, `--region` to backend codes, and the AWS
1010
+ account and cloud region for the canvas.
1011
+ 2. Validate the name locally, then ask the backend's duplicate check.
1012
+ 3. Print a summary and ask for confirmation (`-y` skips it; `--no-input`
1013
+ without `-y` exits 5).
1014
+ 4. Register the resource (`POST /infrastructures`).
1015
+ 5. Add it to the transaction queue and approve the item.
1016
+ 6. Start the deployment (`POST /transaction-queue/deploy`). The backend
1017
+ opens a pull request and applies it through its workflow; the command
1018
+ returns as soon as the workflow is accepted.
1019
+ 7. With `--wait`, poll the workflow until it finishes or fails.
1020
+
1021
+ The output carries the resource code, the queue item code, and the workflow
1022
+ id or pull-request URL. Re-running the same create is refused as a
1023
+ duplicate (exit 7); use `describe` to see the existing resource.
1024
+
1025
+ ## 9. Scripting examples
1026
+
1027
+ ```
1028
+ # JSON for every service, filtered with jq
1029
+ devlift services list -o json | jq '.[] | select(.status=="active") | .service_name'
1030
+
1031
+ # Fully non-interactive create; fails instead of prompting
1032
+ devlift s3 create --name my-logs --app core --env stage --region mumbai \
1033
+ --versioning --no-replication --no-input -y -o json
1034
+
1035
+ # The workflow id from that JSON, followed to the end
1036
+ devlift deployment status "$(devlift s3 create ... --no-input -y -o json | jq -r .workflow_id)" --wait
1037
+
1038
+ # Use a CI token instead of a browser login
1039
+ DEVLIFT_TOKEN=... devlift whoami -o json
1040
+
1041
+ # Follow a deployment started earlier
1042
+ devlift deployment status deploy-example-9f8e7d6c5b4a --wait
1043
+ ```
1044
+
1045
+ ## 10. Troubleshooting
1046
+
1047
+ - **exit 2, "Not signed in"**: run `devlift login`. If you set
1048
+ `DEVLIFT_TOKEN`, it is used as-is and never refreshed.
1049
+ - **"Profile 'default' is signed in to X, but this command targets Y"**: an
1050
+ environment variable or `--endpoint-url` changed the backend. Unset
1051
+ `DEVLIFT_BASE_URL`, or `devlift login` against the new backend.
1052
+ - **"Could not connect"**: the backend at the profile's URL is not running.
1053
+ Check `devlift configure --list`.
1054
+ - **`--wait` reports the status service is busy**: the backend's workflow
1055
+ status route is unavailable; the deployment itself continues. Check later
1056
+ with `devlift deployment status <workflow-id>`.
1057
+ - **Table output looks like JSON**: stdout is not a terminal, so JSON is the
1058
+ default. Force a table with `-o table`.
1059
+
1060
+ ## 11. Not available yet
1061
+
1062
+ Not available from the CLI, by design: **variables and secrets** (their
1063
+ draft lives partly in a store only the secret service may write, and
1064
+ secret values must never pass through a shell), use the dashboard's
1065
+ Variables tab. Planned but not implemented: `data query` and an interactive
1066
+ `shell`.