Alchemy Module
Documentation for the alchemy module
Shared Alchemy runtime and codegen for hosted deployments.
This module provides:
- Shared Alchemy configuration under
stackpanel.deployment.alchemy.* - Generated
@gen/alchemypackage (createApp, state store factory, helpers, SOPS-aware file module) - First-run Cloudflare setup script (
alchemy:setup) - Provider-scoped deploy helper (
alchemy:deploy) - Optional bootstrap flow to solve Cloudflare state-store chicken-and-egg
Files
default.nix- module entrypoint (imports options + codegen)options.nix- option schema forstackpanel.deployment.alchemy.*codegen.nix- generates package files/scripts and wires devshell envtemplates/- TypeScript and shell templates used by codegen (includingfile.tmpl.ts)
Core Options
stackpanel.deployment.alchemy = {
enable = true;
# Shared app defaults
app-name = "my-project";
stage = null;
# State store for alchemy state
state-store.provider = "auto"; # cloudflare | filesystem | auto
# Deploy DX
deploy = {
enable = true;
token-scopes = "profile"; # profile | god
auto-provision-state-store = true;
run-file = "alchemy.run.ts";
};
};Secrets Integration
When these paths are set, values are injected into the devshell environment:
stackpanel.deployment.alchemy.secrets = {
cloudflare-token-sops-path = "ref+sops://.stack/secrets/vars/common.sops.yaml#/cloudflare-api-token";
state-token-sops-path = "ref+sops://.stack/secrets/vars/common.sops.yaml#/alchemy-state-token";
sops-group = "common";
};Generated Scripts
When stackpanel.deployment.alchemy.deploy.enable = true:
-
alchemy:setup- Runs
alchemy configure+alchemy login - Creates Cloudflare token via
alchemy util create-cloudflare-token - Generates
ALCHEMY_STATE_TOKEN - Stores both tokens via
secrets:set - Optionally bootstraps Cloudflare state store worker
- Runs
-
alchemy:deploy- Loads tokens from env/secrets
- Auto-runs
alchemy:setupif Cloudflare is not configured - Executes
bunx alchemy deploy <run-file> --stage <stage>
Generated File Module (SOPS-aware)
The generated package now includes @gen/alchemy/file composed with Alchemy's FS provider (File from alchemy/fs) and a secret-aware read layer:
AlchemyFile- re-export of Alchemy'sFileresource fromalchemy/fsAlchemyFileModule- composes plain-file reads withref+sops://...resolutioncreateDefaultSopsResolver()- default resolver backed bysops-age(pure JS)createAlchemyFileModule()- convenience factorysecret(value)- wraps plaintext into Alchemy-native secret values (alchemy.secret(...))readSecret(pathOrRef)- reads from plain file or SOPS ref, then returns an Alchemy-native secret wrapper
Example:
import { AlchemyFile, createAlchemyFileModule } from "@gen/alchemy/file";
const files = createAlchemyFileModule();
// Use Alchemy FS File resource (create/update/delete managed file)
await AlchemyFile("config.txt", {
path: "config.txt",
content: "hello from alchemy fs",
});
// Plain file read
const packageJson = await files.readFile("package.json");
// SOPS ref read (resolved through sops-age)
const apiToken = await files.readFile(
"ref+sops://.stack/secrets/vars/common.sops.yaml#/cloudflare-api-token",
);
// Native Alchemy secret wrappers
const apiTokenSecret = await files.readSecret(
"ref+sops://.stack/secrets/vars/common.sops.yaml#/cloudflare-api-token",
);
const dbPasswordSecret = files.secret("local-dev-password");Bootstrap State Store
If deploy.auto-provision-state-store = true, codegen emits:
packages/gen/alchemy/bootstrap.run.ts
alchemy:setup uses that file to provision and verify CloudflareStateStore
with filesystem-backed state first, then normal deploys use shared Cloudflare
state.
Typical Usage
nix develop --impure
alchemy:deploy stagingUse stackpanel deploy for the canonical deploy flow. alchemy:deploy is a
provider-scoped helper for working directly with the generated Alchemy runtime.