From darkmatter-skills
Configure, develop, deploy, review, and troubleshoot Alchemy v2 infrastructure for Darkmatter TypeScript/Effect apps. Triggers for alchemy.run.ts, alchemy dev, public preview URLs, webhook testing, Cloudflare/AWS providers, stages, profiles, state stores, bindings, CI deploys, or examples from alchemy-run/alchemy-effect. Prefer Alchemy for new org deploys. Do NOT trigger for unrelated blockchain Alchemy APIs unless the user explicitly means alchemy.run.
How this skill is triggered — by the user, by Claude, or both
Slash command
/darkmatter-skills:alchemyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when configuring, developing, deploying, or debugging infrastructure with Alchemy v2 and the Effect-native patterns from `alchemy-run/alchemy-effect`. Alchemy treats infrastructure as an Effect program: stacks declare resources, providers supply cloud integrations, local dev can run app code locally against real cloud resources, and deploys converge declared resources into live c...
Use this skill when configuring, developing, deploying, or debugging infrastructure with Alchemy v2 and the Effect-native patterns from alchemy-run/alchemy-effect. Alchemy treats infrastructure as an Effect program: stacks declare resources, providers supply cloud integrations, local dev can run app code locally against real cloud resources, and deploys converge declared resources into live cloud state.
Alchemy is the preferred Darkmatter path for new TypeScript/Effect deployable infrastructure. Reach for it before ad hoc provider scripts, hand-written Wrangler/Terraform glue, or one-off deploy workflows unless a repo has an explicit exception or an existing production system that should not be disturbed.
Alchemy v2 and alchemy-effect move quickly. Verify upstream docs before changing deploy or dev code, especially package versions, provider APIs, alchemy dev behavior, and CLI flags. This skill carries the upstream alchemy-run/alchemy-effect repo as a local submodule at reference/alchemy-effect for source, examples, and agent instructions.
alchemy.run.ts file.alchemy dev, local Workers, public preview URLs, hot reload, or webhook testing.alchemy-run/alchemy-effect/examples.alchemy.run.alchemy-effect repo. Use the upstream repo's AGENTS.md directly, then use this skill only for deploy-configuration implications.effect-typescript.Before editing, inspect the current docs or local installed package:
bun alchemy --help
bun alchemy dev --help
bun pm ls alchemy effect @effect/platform-bun @effect/platform-node
Use these upstream references first:
https://v2.alchemy.run/getting-started/ for the current install command, first stack shape, and deploy flow.https://v2.alchemy.run/concepts/stack/ for stack names, outputs, stages, cross-stack references, and state isolation.https://v2.alchemy.run/concepts/local-development/ and https://v2.alchemy.run/tutorial/part-4/ for alchemy dev.https://v2.alchemy.run/concepts/state-store/ for where state is persisted.https://v2.alchemy.run/guides/ci/ for non-interactive deploys and PR preview environments.reference/alchemy-effect/AGENTS.md for upstream Effect-native infrastructure conventions.reference/alchemy-effect/README.md for the current project overview and install shape.reference/alchemy-effect/examples/ for concrete app patterns.reference/alchemy-effect/packages/alchemy/src/ for provider/resource implementation patterns.If upstream conflicts with this skill, follow upstream and mention the drift.
If the submodule is missing in a fresh checkout, initialize it before relying on local references:
git submodule update --init skills/alchemy/reference/alchemy-effect
Treat the submodule as read-only during app/deploy work. Edit it only when the user explicitly asks for an upstream alchemy-effect contribution, and then follow reference/alchemy-effect/AGENTS.md as the repo-local authority.
Prefer Bun unless the project already standardizes on another package manager. Current Alchemy v2 docs recommend Bun or Node.js 22+ and install Alchemy with Effect platform packages:
bun add "[email protected]" "effect@>=4.0.0-beta.66 || >=4.0.0" "@effect/platform-bun@>=4.0.0-beta.66 || >=4.0.0" "@effect/platform-node@>=4.0.0-beta.66 || >=4.0.0"
Pin package versions according to the existing repo policy. If a repo already uses a catalog, workspace protocol, or lockfile-only upgrade lane, follow that instead of pasting the docs command blindly.
Before adding Alchemy to an existing repo, inspect the current setup:
rg -n "alchemy|alchemy.run|alchemy dev|alchemy deploy" package.json bun.lock package-lock.json pnpm-lock.yaml yarn.lock .github scripts . --glob '!node_modules'
find . -name 'alchemy.run.ts' -o -name 'alchemy.run.js'
If the repo does not already have Alchemy configured, ask the user whether they want Alchemy set up for local development as well as deploys. Do not silently create a deploy-only setup: alchemy dev changes the local workflow by provisioning real cloud resources, exposing public preview URLs, and enabling webhook/OAuth callback testing against local code. If the user says yes, include the dev script, dev-stage/profile guidance, public preview output handling, and webhook test notes in the first implementation.
Add scripts that make the stack path explicit when the repo has more than one app:
{
"scripts": {
"dev": "alchemy dev ./alchemy.run.ts",
"deploy": "alchemy deploy ./alchemy.run.ts",
"destroy": "alchemy destroy ./alchemy.run.ts",
"deploy:prod": "alchemy deploy ./alchemy.run.ts --stage prod --profile prod"
}
}
Use plain bun alchemy deploy only in tiny repos where alchemy.run.ts at the root is unambiguous.
For Darkmatter TypeScript/Effect projects, prefer Alchemy for deployable infrastructure:
alchemy dev or an Alchemy-managed tunnel/preview flow instead of separate ngrok/Cloudflare Tunnel scripts when Alchemy can provide the route.Every deploy file should default-export Alchemy.Stack(name, options, Effect.gen(...)).
import * as Alchemy from "alchemy";
import * as Cloudflare from "alchemy/Cloudflare";
import * as Effect from "effect/Effect";
export default Alchemy.Stack(
"MyApp",
{
providers: Cloudflare.providers(),
state: Cloudflare.state(),
},
Effect.gen(function* () {
const bucket = yield* Cloudflare.R2Bucket("Bucket");
return {
bucketName: bucket.bucketName,
};
}),
);
The load-bearing pieces are:
Cloudflare.providers() or AWS.providers().Cloudflare.state(); simple AWS examples may use Alchemy.localState().yield*, compose dependencies through references and bindings, and return useful outputs for smoke checks.When authoring or refactoring an Alchemy provider in a Darkmatter app/repo, mirror the canonical alchemy-run/alchemy-effect/packages/alchemy/src/Cloudflare organization instead of putting resource contracts, HTTP clients, schemas, helpers, and provider lifecycle in one large file. Before writing provider code, inspect the relevant upstream provider directory (for example Cloudflare/Providers.ts, Cloudflare/KV/KVNamespace.ts, and adjacent *Binding.ts/index.ts files) and keep the same separation of concerns.
Canonical shape for a provider namespace such as src/Verda/:
ResourceName.ts contains only the public Resource type, props, attributes, JSDoc/examples, and Resource<ResourceName>("Namespace.ResourceName"). It should not contain the HTTP client or reconcile helpers.ResourceNameProvider.ts contains Provider.effect(ResourceName, Effect.gen(...)) and the lifecycle methods (read, diff, reconcile, delete). Keep provider-local adoption/reconcile logic readable and delegate API calls/selection/status helpers to sibling modules.Providers.ts defines class Providers extends Provider.ProviderCollection<Providers>()("Namespace") {}, ProviderRequirements, and providers(). The Provider.collection([...]) list contains resource tokens/policies (for example GpuInstance), while implementation layers are supplied with .pipe(Layer.provide(...)), matching Cloudflare's Providers.ts. Do not put already-provided provider layers inside Provider.collection.Client.ts / provider SDK modules expose Effect Context.Service clients and Layer.effect live implementations. Decode all unknown provider responses with Schema at this boundary and return typed domain objects.Config.ts / Credentials.ts centralize Config.string, Config.redacted, defaults, and auth/profile resolution. Secrets stay redacted and are provided through layers, not resource props or physical names.Errors.ts, Types.ts, Wire.ts, Mapping.ts, Status.ts, Selection.ts, and other focused helpers are preferred over multi-hundred-line resource files when they isolate typed errors, wire schemas, pure selection logic, and domain mapping.index.ts re-exports the namespace's public surface so app code imports ./ProviderNamespace/index.ts rather than a monolithic resource file.Testing convention for provider refactors: extract pure helpers where possible (selection, name normalization, status classification, mapping) and write focused Vitest tests for them before moving production code. For Effect-dependent provider lifecycle, test through Layers/fakes where practical; otherwise typecheck with bun tsc -b and avoid changing behavior during layout-only refactors.
Use alchemy dev for the default local loop when the app can run through Alchemy:
bun alchemy dev ./alchemy.run.ts
Alchemy dev mode deploys infrastructure to the real cloud provider, runs supported app code locally, and hot reloads on file changes. For Cloudflare Workers, the Worker runs locally in workerd while a cloud proxy routes requests between public cloud endpoints and the local process.
This matters for webhook work: external services need a public URL, while the developer needs local code, breakpoints, and hot reload. Prefer an Alchemy dev/preview URL for webhook callbacks when available, then configure the third-party webhook target to that URL for the session or preview stage.
Use explicit dev ports when the app has multiple local services or a fixed callback URL:
export const Worker = Cloudflare.Worker("Worker", {
main: "./src/worker.ts",
dev: {
port: 3000,
},
});
Dev-mode rules:
alchemy dev as real resources, not emulators.Treat stage and profile as part of the deployment contract:
--stage prod, --stage staging, or --stage pr-123 for shared environments.~/.alchemy/profiles.json; non-interactive CI must have profiles or provider credentials prepared before deploy.--stage prod and the intended --profile.Useful commands:
bun alchemy deploy ./alchemy.run.ts --stage dev_cm
bun alchemy deploy ./alchemy.run.ts --stage prod --profile prod
bun alchemy destroy ./alchemy.run.ts --stage pr-123 --profile preview
alchemy login --configure
alchemy login --profile prod --configure
Never use Date.now() or random timestamps in physical names. Let Alchemy generate names from stack, stage, and logical id, or use a deterministic name tied to the environment.
Prefer bindings over manually threading deployed identifiers through environment variables. A binding is deploy-time data attached to a runtime resource so the function/worker receives exactly the infrastructure dependency it needs.
For Cloudflare, bind resources directly into a Worker or Vite app:
export const DB = Cloudflare.D1Database("DB");
export const Bucket = Cloudflare.R2Bucket("Bucket");
export const Worker = Cloudflare.Worker("Worker", {
main: "./src/worker.ts",
bindings: {
DB,
Bucket,
},
});
For Effect-native runtimes, keep runtime services and deploy wiring separate. Alchemy owns cloud resources and bindings; Effect services own runtime behavior. Use Layers at the stack boundary when the resource requires a runtime implementation.
Binding.Service + Binding.PolicyEvery Alchemy capability ships as a pair of layers:
Binding.Service — runtime SDK wrapper, provided on the Function/Worker Effect (bundled into the deployed artifact).Binding.Policy — deploy-time IAM/binding attachment, provided on the Stack via AWS.providers()(), never bundled.For Cloudflare, the worker bindings: { ... } field handles both sides automatically. For AWS, the Service layer goes on the Function and the Policy layer must be enabled at the stack level. The default AWS.providers() already enables the common policy layers; only reach for AWS.providers()(extraLayers) when you author or override a capability.
Alchemy.RuntimeContext on runtime-only methodsBindings expose a typed callable whose inner Effect carries an Alchemy.RuntimeContext requirement. Treat that as a colored function:
RuntimeContext.RuntimeContext and only runs inside a deployed Function/Worker.If a service wraps a binding and accidentally leaks WorkerEnvironment / Lambda.FunctionEnvironment into its interface, that service becomes cloud-coupled. Resolve cloud env services once during Layer construction and close over them — do not expose them as requirements on consumer Effects.
Alchemy stacks and Worker/Function bodies run as Effect programs. Do not reach for raw async or Node primitives inside Effect.gen:
| Don't | Do |
|---|---|
import fs from "node:fs/promises" | const fs = yield* FileSystem.FileSystem |
await fs.readFile(p, "utf8") | yield* fs.readFileString(p) |
import path from "pathe" / node:path | const path = yield* Path.Path |
await fetch(...) | yield* HttpClient.HttpClient + HttpClientRequest |
new Promise((res) => setTimeout(res, ms)) | yield* Effect.sleep(Duration.millis(ms)) |
Effect.promise(() => listSqlFiles(dir)) | Make the helper itself return an Effect |
Sync, CPU-only Node APIs (crypto.createHash, process.cwd, Buffer, TextEncoder) must still be wrapped in Effect.sync(() => …) (or Effect.try if they can throw) so the call participates in the Effect runtime — tracing, interruption, and the error channel all depend on it.
const hash = yield * Effect.sync(() => crypto.createHash("sha256").update(input).digest("hex"));
const cwd = yield * Effect.sync(() => process.cwd());
This applies to stack bodies, custom resource helpers, and tests. Tests must use FileSystem.FileSystem / Path.Path for any file/path access.
Polling rules for deploy/runtime tests:
Effect.repeat with Schedule + an until predicate and a bounded times: N cap.while (Date.now() < deadline) loops — they ignore interruption and leak into the vitest timeout.// good — declarative, bounded, interruption-safe
const value =
yield *
fetchValue.pipe(
Effect.repeat({
schedule: Schedule.spaced("5 seconds"),
until: (v) => v.ready,
times: 36,
}),
);
When authoring custom Alchemy providers/resources, add provider-style tests with alchemy/Test/Vitest rather than only unit-testing helper functions. These tests exercise the real Alchemy plan/apply engine, private scratch state, repeated deploys, and destroy behavior in one Effect program.
Use this shape for resource lifecycle tests:
import * as Test from "alchemy/Test/Vitest";
import { expect } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as ProviderNamespace from "../src/ProviderNamespace/index.ts";
const { test } = Test.make({ providers: ProviderNamespace.providers() });
test.provider("create, update, and delete resource", (stack) =>
Effect.gen(function* () {
const client = yield* ProviderNamespace.Client;
const created = yield* stack.deploy(
Effect.gen(function* () {
return yield* ProviderNamespace.Resource("TestResource", {
name: "v1",
});
}),
);
expect(created.resourceId).toBeDefined();
// Verify live provider state through the provider SDK/client, not only
// through returned attributes.
const live1 = yield* client.get(created.resourceId);
expect(live1.name).toBe("v1");
const updated = yield* stack.deploy(
Effect.gen(function* () {
return yield* ProviderNamespace.Resource("TestResource", {
name: "v2",
});
}),
);
expect(updated.resourceId).toBe(created.resourceId);
const live2 = yield* client.get(updated.resourceId);
expect(live2.name).toBe("v2");
yield* stack.destroy();
}),
);
Provider test rules:
const { test } = Test.make({ providers: Namespace.providers() }).test.provider, stack.deploy(...), repeated deploy, and stack.destroy().test.provider body so Alchemy state is shared across deploys..alchemy/ or shared state from provider tests.const client = yield* Client before returning lifecycle methods), matching Cloudflare providers. Do not leave yield* Client requirements inside read/diff/reconcile/delete unless the effect is explicitly provided there; test.provider should catch this with a “Service not found” failure.alchemy deploy (for example env-method credentials or an alchemy login test profile). Skip or gate the test when credentials are absent; do not commit secrets.Always type-check before committing changes that touch a stack, custom resource, or runtime binding:
bun tsc -b
This runs the workspace build in project-reference mode. CI fails on type errors, so this is non-negotiable. When stale artifacts or dependency drift cause unexplained failures, fall back to a clean rebuild:
bun run build # clean + tsc + build the alchemy package
bun build:clean # full reset: clean . + bun i + build + download env
bun build:clean removes untracked files (preserving .env), reinstalls dependencies, rebuilds, and refreshes environment files. Treat it as a recovery command, not a routine one.
Do not commit provider tokens, API keys, generated profiles, or state files unless the repo explicitly owns a remote state backend and the file is designed for version control.
Use these rules:
.env convention.CI should be deterministic and non-interactive:
Example CI command shape:
bun install --frozen-lockfile
bun tsc -b
bun alchemy deploy ./alchemy.run.ts --stage "$ALCHEMY_STAGE" --profile "$ALCHEMY_PROFILE"
If a deploy failure starts in provider auth, profile lookup, state lock/state persistence, or missing CI secrets, fix that layer before changing resource code.
Public previews are an expected Alchemy workflow:
--stage pr-<number> and post the resulting app URL back to the PR when the repo has GitHub access.alchemy dev when a webhook or collaborator needs to reach a public URL backed by local code.curl.Always verify the real deployed target when the request concerns deploy behavior:
bun alchemy deploy ./alchemy.run.ts --stage "$STAGE" --profile "$PROFILE"
curl -fsS "$DEPLOYED_URL"
For local dev behavior, run the dev server and hit both local and public/proxied URLs when Alchemy prints both:
bun alchemy dev ./alchemy.run.ts --stage "$STAGE" --profile "$PROFILE"
curl -fsS "$LOCAL_URL"
curl -fsS "$PUBLIC_PREVIEW_URL"
For Effect-native workers/functions, prefer a deployed fixture or smoke route over a purely local assertion. Fresh Workers, Lambda URLs, queues, cron, and DNS often need bounded retries for propagation; use Effect.retry or Effect.repeat with Schedule, not manual polling loops.
--stage and --profile.alchemy dev is configured for local loops that need real cloud resources, public preview URLs, or webhook testing.Schedule for retries instead of raw promises, bare SDK calls inside business logic, or Date.now() polling.alchemy-effectIf a darkmatter repo needs a missing provider feature, the path is usually a PR upstream rather than a local fork. Two conventions matter when you open that PR:
feat(aws/s3): add bucket lifecycle rules, fix(website): mobile theme metas).# or ## heading — GitHub already renders the title above it. Use ### at most, and only when the description genuinely has multiple sections. Lead with a short prose summary or a code snippet that shows the new shape; cut anything that restates what the diff already shows. Never include a "Test plan" or checklist.--body-file to gh pr create / gh pr edit. Heredoc/--body "$(cat …)" mangles backticks across gh versions.If you edit tutorial docs under website/src/content/docs/tutorial/, one concept gets one ## heading, one diff snippet, and one prose paragraph. "Two/three things just happened" + a numbered list is the smell that says split the snippet.
If you edit resource JSDoc, run bun generate:api-reference to refresh website/src/content/docs/providers/{Cloud}/{Resource}.md. The generated markdown is overwritten on every regeneration — never hand-edit it.
reference/provider-implementation.md has the detailed conventions you need before touching anything under packages/alchemy/src/ upstream.
reference/upstream-concepts.md — Alchemy v2 and alchemy-effect concepts applied during app/deploy work.reference/examples.md — alchemy-run/alchemy-effect/examples directories mapped to common deploy scenarios.reference/provider-implementation.md — file system, reconciler doctrine, capability shape, tags, runtime context, test fixtures, JSDoc/docs, and tutorial standards for upstream provider contributions.reference/alchemy-effect/ — pinned git submodule of alchemy-run/alchemy-effect for current source, examples, docs, and upstream agent instructions.npx claudepluginhub darkmatter/skills --plugin darkmatter-skillsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.