1---
2title: 2.0.0-beta.77 - Profiles, CLI & Workers Tracing
3date: 2026-09-08T09:00:00Z
4category: release
5excerpt: v2.0.0-beta.77 redesigns the CLI — alchemy login becomes alchemy profile, profiles are stored as a folder per profile with a file per provider, and every TUI action has a non-interactive twin for agents and CI — and adds Cloudflare.Telemetry() for tracing Workers in Workers Observability. Also dependency-aware dev reloads, cron schedules on Workflows, provisioned ElastiCache, and private/pre-deploy/local-context Railway services.
6---
7
8beta.77 redesigns the CLI. `alchemy login` becomes
9**`alchemy profile`**, a dashboard for every connected account
10whose every action is also a non-interactive subcommand; profiles
11are stored as **a folder per profile with a file per provider**
12instead of a central `profiles.json`; and `alchemy dev` gets a
13plan/output widget you can flip with `←/→`. Outside the CLI,
14**`Cloudflare.Telemetry()`** sends Effect spans to Workers
15Observability with no OTLP setup, Workflows take cron schedules,
16and AWS gains provisioned ElastiCache.
17
18Here is the new CLI on a fresh machine, start to finish:
19
20<video controls muted playsinline preload="metadata" poster="/blog/beta-77/alchemy-cli-poster.png" src="/blog/beta-77/alchemy-cli.mp4" style="width: 100%; border-radius: 8px;"></video>
21
22_`alchemy profile` → `alchemy dev` → `alchemy deploy` → `alchemy drift` → `alchemy destroy`. The same walkthrough, frame by frame, is below._
23
24:::caution
25**Breaking changes**
26
27- **`alchemy deploy` defaults to `live_$USER`**
28 ([#1421](https://github.com/alchemy-run/alchemy/pull/1421)) —
29 `plan`, `destroy`, `logs`, and `drift` follow; `alchemy dev`
30 keeps `dev_$USER`. Pass `--stage dev_sam` to keep deploying to
31 an old target. The stage env var is now `ALCHEMY_STAGE`, not
32 `STAGE` ([#1433](https://github.com/alchemy-run/alchemy/pull/1433)).
33- **Commands moved**
34 ([#1234](https://github.com/alchemy-run/alchemy/pull/1234),
35 [#1235](https://github.com/alchemy-run/alchemy/pull/1235)). The
36 old names print a migration hint instead of running:
37
38 | Old | New |
39 | ---------------------------- | ------------------------------------- |
40 | `alchemy login` | `alchemy profile` |
41 | `alchemy login --configure` | `alchemy profile edit` |
42 | `alchemy profile clear` | `alchemy profile delete` |
43 | `alchemy tail` | `alchemy logs --follow` |
44 | `alchemy sync` | `alchemy drift --repair` |
45 | `alchemy destroy --dry-run` | `alchemy plan --destroy` |
46 | `alchemy aws` / `cloudflare` | `alchemy provider aws` / `cloudflare` |
47
48- **The AWS `local` auth method is removed**
49 ([#1480](https://github.com/alchemy-run/alchemy/pull/1480)) —
50 `alchemy dev` wires the local emulator itself.
51- **distilled operations are `verbNoun`**
52 ([#1517](https://github.com/alchemy-run/alchemy/pull/1517)) —
53 `railway.serviceCreate` is now `railway.createService`. Only
54 affects code that calls a distilled client directly.
55:::
56
57## 1. Connect an account
58
59`alchemy login` is now **`alchemy profile`**
60([#1234](https://github.com/alchemy-run/alchemy/pull/1234),
61[#1235](https://github.com/alchemy-run/alchemy/pull/1235),
62[#1547](https://github.com/alchemy-run/alchemy/pull/1547)). On a
63fresh machine it opens on an empty `default` profile:
64
65![alchemy profile on a fresh machine: one empty default profile](/blog/beta-77/profile-empty.png)
66
67`e` lists every provider. Space queues a change on the focused
68row — here Cloudflare goes to `add` — and Enter runs the queue:
69
70![the edit menu with Cloudflare queued to add](/blog/beta-77/profile-add.png)
71
72Each provider asks only what it needs. Cloudflare offers OAuth or
73a token:
74
75![choosing OAuth as the Cloudflare authentication method](/blog/beta-77/profile-method.png)
76
77OAuth opens the browser; you approve the grant there:
78
79![Cloudflare's consent page for Alchemy](/blog/beta-77/oauth-consent.png)
80
81Back on the dashboard the provider is a row with its live details:
82
83![the dashboard after connecting: Cloudflare via oauth, token expiry, account id](/blog/beta-77/profile-connected.png)
84
85Profiles are tabs, `←/→` switches between them, and every profile
86holds one connection per provider — a `work` profile with a
87Cloudflare token next to a `default` with OAuth + AWS SSO:
88
89![two profiles, each with several providers](/blog/beta-77/profile-dashboard.png)
90
91Every key on the dashboard is also a subcommand, and the
92subcommands never prompt:
93
94```sh
95alchemy profile create work
96alchemy profile edit --profile work --add cloudflare # runs the OAuth flow
97alchemy profile refresh --provider cloudflare
98alchemy profile rename work staging
99alchemy profile delete staging --yes
100```
101
102For agents and CI, token-shaped methods are configured entirely
103from flags. `--set` takes a literal, an environment variable, or
104stdin, so secrets never touch shell history:
105
106```sh
107alchemy profile edit --add cloudflare --method stored \
108 --set apiToken=env:CLOUDFLARE_API_TOKEN \
109 --set accountId=0123456789abcdef0123456789abcdef
110
111op read op://vault/neon/key | alchemy profile edit --add neon --method stored --set apiKey=-
112```
113
114Under the hood, the central `profiles.json` and the separate
115`credentials/` tree are gone. A profile is a directory and each
116provider is one file holding its method, config, and credentials
117together. Existing installs migrate on first run, with the old
118layout backed up to `~/.alchemy/.profiles-v0-<timestamp>/`
119([#1429](https://github.com/alchemy-run/alchemy/pull/1429)):
120
121```text
122~/.alchemy/profiles/
123├─ default/
124│ ├─ cloudflare.json # method: oauth, accountId, tokens, scopes
125│ └─ aws.json # method: sso, ssoProfile
126└─ work/
127 └─ cloudflare.json # method: stored, apiToken, accountId
128```
129
130Only `profile` commands log in. `deploy`, `plan`, and `dev` never
131open a wizard — a missing credential fails with the exact
132`alchemy profile edit --add …` to run. Explicit provider
133environment variables (`CLOUDFLARE_API_TOKEN`, `AWS_ACCESS_KEY_ID`,
134…) take precedence over the profile, per provider, and the CLI
135logs which ones it used
136([#1520](https://github.com/alchemy-run/alchemy/pull/1520)).
137
138Docs: [profile](/cli/profile) · [Profiles](/environments/profiles)
139· [CI](/environments/ci).
140
141## 2. `alchemy dev`
142
143The stack in the video is a Worker bound to a KV namespace and an
144R2 bucket:
145
146```typescript
147export default class Api extends Cloudflare.Worker<Api>()(
148 "Api",
149 { main: import.meta.url },
150 Effect.gen(function* () {
151 const visits = yield* Cloudflare.KV.ReadWriteNamespace(Visits);
152 const uploads = yield* Cloudflare.R2.ReadWriteBucket(Uploads);
153 return { fetch: /* … */ };
154 }).pipe(
155 Effect.provide([
156 Cloudflare.KV.ReadWriteNamespaceBinding,
157 Cloudflare.R2.ReadWriteBucketBinding,
158 ]),
159 ),
160) {}
161```
162
163`alchemy dev` emulates all three locally. Logs scroll above; the
164widget at the bottom shows the stack's output:
165
166![alchemy dev running, output widget showing the local url and bucket](/blog/beta-77/dev-output.png)
167
168`←/→` flips the widget to the last applied plan — the same
169component `deploy`, `drift`, and `destroy` render
170([#1458](https://github.com/alchemy-run/alchemy/pull/1458),
171[#1492](https://github.com/alchemy-run/alchemy/pull/1492)). `↑/↓`
172scrolls it, `p` hides it:
173
174![the same widget flipped to the plan: 3 created](/blog/beta-77/dev-plan.png)
175
176Save a file and only the affected resource restarts. Reloads follow
177the import graph from your entrypoint — sibling `infra/` and `src/`
178trees included — and only fire when a file's contents actually
179change ([#1461](https://github.com/alchemy-run/alchemy/pull/1461),
180[#1477](https://github.com/alchemy-run/alchemy/pull/1477)):
181
182![after editing the worker: 1 updated, 2 no change](/blog/beta-77/dev-reload.png)
183
184## 3. `alchemy deploy`
185
186`deploy` shows the plan first. Bindings appear under the resource
187they attach to, and the prompt defaults to go:
188
189![the deploy plan: 3 to create, 2 binding changes, Deploy highlighted](/blog/beta-77/deploy-confirm.png)
190
191Provider logs stream while it runs; the plan ticks over as each
192resource lands and the stack's outputs print at the end:
193
194![stack deployed: url and bucket outputs](/blog/beta-77/deploy-done.png)
195
196```sh
197$ curl -s https://demo-api-live-samgoodwin-sthgco4pjlba35wc.testing-2b2.workers.dev
198{"message":"Hello from Alchemy","visits":1}
199```
200
201## 4. `alchemy drift`
202
203Someone renames the KV namespace in the Cloudflare dashboard.
204`alchemy drift` re-reads the cloud and diffs it against what was
205deployed — the changed attribute renders inline, and `Repair`
206puts it back:
207
208![drift detected: title changed on the KV namespace, Repair highlighted](/blog/beta-77/drift-detected.png)
209
210![the repair applied: 1 updated](/blog/beta-77/drift-repaired.png)
211
212`alchemy drift --repair` does the same without the prompt.
213
214## 5. `alchemy destroy`
215
216Destructive prompts default to `Cancel`:
217
218![the destroy plan: 3 to delete, Cancel highlighted](/blog/beta-77/destroy-confirm.png)
219
220![stack destroyed](/blog/beta-77/destroy-done.png)
221
222## Without a TTY
223
224In CI, a pipe, or a coding agent, the same commands print plain
225append-only lines instead of the TUI — or force it with
226`ALCHEMY_PLAIN=1` / `--no-input`
227([#1423](https://github.com/alchemy-run/alchemy/pull/1423),
228[#1426](https://github.com/alchemy-run/alchemy/pull/1426)). Same
229deploy as above, plain:
230
231![ALCHEMY_PLAIN=1 alchemy deploy --yes: timestamped log lines for the plan, each create, Done: 3 succeeded, and the outputs](/blog/beta-77/deploy-plain.png)
232
233Exit codes are `0` done / `1` failed or declined / `130` cancelled,
234and every run writes a debug log under `~/.alchemy/logs` — the first
235thing to attach to a bug report.
236
237Docs: [CLI](/cli) · [dev](/cli/dev) · [deploy](/cli/deploy) ·
238[drift](/cli/drift).
239
240## `Cloudflare.Telemetry()`
241
242Effect spans from a Worker now land in
243[Workers Observability](https://developers.cloudflare.com/workers/observability/)
244([#1444](https://github.com/alchemy-run/alchemy/pull/1444)).
245Provide the Layer on the Worker and your `Effect.withSpan` frames
246show up in the Cloudflare dashboard, nested under the platform's
247own spans:
248
249```typescript
250export default Cloudflare.Worker(
251 "Api",
252 { main: import.meta.url },
253 Effect.gen(function* () {
254 return {
255 fetch: Effect.gen(function* () {
256 const user = yield* kv.get("user:1").pipe(Effect.withSpan("load.user"));
257 return yield* respond(user);
258 }).pipe(Effect.withSpan("handle")),
259 };
260 }).pipe(Effect.provide(Cloudflare.Telemetry())),
261);
262```
263
264```text
265http.server GET
266└─ handle
267 ├─ load.user
268 │ └─ kv_get
269 └─ respond
270```
271
272Cloudflare owns sampling and export, so there is no OTLP endpoint
273and nothing to flush. It composes with `Axiom.Telemetry` for the
274same waterfall in both. The Worker's compatibility date must be
275`2026-07-28` or later.
276
277Docs: [Native Workers tracing](/cloudflare/observability/workers-tracing).
278
279## Workflows: cron schedules
280
281Cloudflare Workflows take wrangler-compatible `schedules`, so a
282cron expression creates instances natively — no Cron Trigger
283Worker calling `workflow.create()`
284([#1491](https://github.com/alchemy-run/alchemy/pull/1491)):
285
286```typescript
287export default class Hourly extends Cloudflare.Workflow<Hourly>()(
288 "Hourly",
289 { schedules: ["0 * * * *"] },
290 Effect.gen(function* () {
291 return Effect.fn(function* () {
292 const event = yield* Cloudflare.Workflows.WorkflowEvent;
293 return event.schedule?.cron;
294 });
295 }),
296) {}
297```
298
299## Provisioned ElastiCache
300
301`AWS.ElastiCache` adds `SubnetGroup`, `ReplicationGroup` (Valkey
302and Redis OSS) and `CacheCluster` (Memcached) alongside the
303serverless cache, with `Connect*` bindings that publish the
304endpoint to a Lambda and attach it to the cache's VPC
305([#1422](https://github.com/alchemy-run/alchemy/pull/1422)).
306Thanks [Henning Pokriefke](https://github.com/henningpokriefke)!
307
308```typescript
309const cache = yield* AWS.ElastiCache.ReplicationGroup("Cache", {
310 engine: "valkey",
311 nodeType: "cache.t4g.micro",
312 subnetGroupName: subnetGroup.subnetGroupName,
313 securityGroupIds: [sg.groupId],
314 replicasPerNodeGroup: 1,
315 automaticFailoverEnabled: true,
316});
317
318// inside the Lambda: endpoint env + VPC attachment
319const conn = yield* AWS.ElastiCache.ConnectReplicationGroup(cache, {
320 subnetIds: vpc.privateSubnetIds,
321 securityGroupIds: [sg.groupId],
322});
323```
324
325## Railway: private services, pre-deploy, local contexts
326
327Three additions to `Railway.Service`, all from
328[Dallen Pyrah](https://github.com/dallenpyrah) — thanks!
329([#1439](https://github.com/alchemy-run/alchemy/pull/1439),
330[#1438](https://github.com/alchemy-run/alchemy/pull/1438),
331[#1440](https://github.com/alchemy-run/alchemy/pull/1440))
332
333```typescript
334const worker = yield* Railway.Service("Worker", {
335 project,
336 context: "./worker", // build from a local directory
337 preDeploy: { command: "bun migrate" }, // runs between build and start
338 publicDomain: false, // {name}.railway.internal only
339});
340```
341
342## Also in this release
343
344- **Fly health checks**
345 ([#1463](https://github.com/alchemy-run/alchemy/pull/1463)) —
346 `services[].checks` on `Fly.Machine` and `Fly.Service`. Thanks
347 [Adam Svystun](https://github.com/adamsvystun)! Fly Machines
348 requests also stop hitting `/v1/v1/…`
349 ([#1543](https://github.com/alchemy-run/alchemy/pull/1543)).
350- **Durable Objects: `webSocketError` and caller context**
351 ([#1497](https://github.com/alchemy-run/alchemy/pull/1497),
352 [#1498](https://github.com/alchemy-run/alchemy/pull/1498)) —
353 `blockConcurrencyWhile` / `storage.transaction` run with the
354 calling fiber's context. Thanks [Alex](https://github.com/agcty)!
355 `state.abort(reason, { retryAlarm: false })` stops an interrupted
356 alarm from retrying
357 ([#1374](https://github.com/alchemy-run/alchemy/pull/1374)).
358- **`Cloudflare.Worker.ref` typechecks**
359 ([#1434](https://github.com/alchemy-run/alchemy/pull/1434)).
360 Thanks [pollux](https://github.com/hello-pollux)!
361- **Asset uploads run three buckets at a time**
362 ([#1445](https://github.com/alchemy-run/alchemy/pull/1445)),
363 matching wrangler; workerd bumped to `20260901.1`
364 ([#1442](https://github.com/alchemy-run/alchemy/pull/1442)).
365 Thanks [Rahul Mishra](https://github.com/BlankParticle)!
366- **Workflow steps with `retries` but no `timeout` no longer
367 crash**
368 ([#1416](https://github.com/alchemy-run/alchemy/pull/1416)).
369 Thanks [Lord Coughmann](https://github.com/LordCoughmann)!
370- **Ctrl+C twice force-quits a slow shutdown**
371 ([#1427](https://github.com/alchemy-run/alchemy/pull/1427)).
372- **`_headers` / `_redirects` no longer crash local dev**
373 ([#1418](https://github.com/alchemy-run/alchemy/pull/1418)).
374 Thanks [Michael](https://github.com/deracs)!
375- **Worker Loader `get()` returns a usable stub**
376 ([#1488](https://github.com/alchemy-run/alchemy/pull/1488)) and
377 **`RpcWorker` keeps its logical ID**
378 ([#1487](https://github.com/alchemy-run/alchemy/pull/1487)).
379- **`nuke` shows progress again**
380 ([#1532](https://github.com/alchemy-run/alchemy/pull/1532)), and
381 **`provider cloudflare bootstrap` honors `--profile`**
382 ([#1538](https://github.com/alchemy-run/alchemy/pull/1538)).
383- **PR preview packages live as long as the PR**
384 ([#1511](https://github.com/alchemy-run/alchemy/pull/1511)).
385 Thanks [Michael K](https://github.com/Mkassabov)!
386- **Docs rewritten as a narrative**
387 ([#1470](https://github.com/alchemy-run/alchemy/pull/1470),
388 [#1472](https://github.com/alchemy-run/alchemy/pull/1472)) —
389 [Getting started](/getting-started),
390 [What is Alchemy](/what-is-alchemy), and the Workers hub read
391 top to bottom.
392
393## Where to go next
394
395- [CLI](/cli) · [profile](/cli/profile) · [dev](/cli/dev) · [drift](/cli/drift)
396- [Profiles](/environments/profiles) · [CI](/environments/ci) · [Stages](/environments/stages)
397- [Native Workers tracing](/cloudflare/observability/workers-tracing)
398- [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta77)
399- [Compare v2.0.0-beta.76 → v2.0.0-beta.77](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.76...v2.0.0-beta.77)