# Alchemy Use the Zerops provider for Alchemy to manage projects, services and deployments from TypeScript. Requires `alchemy@2.0.0-beta.84`. Check the [npm package](https://www.npmjs.com/package/@zerops/alchemy) for the latest provider version. ## Install ```sh npm i @zerops/alchemy alchemy@2.0.0-beta.84 effect ``` ```sh bun add @zerops/alchemy alchemy@2.0.0-beta.84 effect ``` ## Auth ```sh export ZEROPS_TOKEN="YOUR_TOKEN" ``` - Create a token in [token management](https://app.zerops.io/settings/token-management). | Variable | Setting | | --- | --- | | `ZEROPS_TOKEN` | Bearer token. | | `ZEROPS_ENDPOINT` | Optional API origin. Defaults to `https://api.app-prg1.zerops.io`. | | `ZEROPS_ORGANIZATION_ID` | Optional organization API `clientId`. Required with multiple memberships. | ### Alchemy Profile Alternative ```sh bunx alchemy profile create zerops bunx alchemy profile edit --profile zerops --add Zerops bunx alchemy deploy --profile zerops ``` - Profile fields are `token`, optional `endpoint`, and optional `organizationId`. ## Minimal Stack ```ts title="alchemy.run.ts" export default Alchemy.Stack("ZeropsApp", { providers: Zerops.providers(), state: Alchemy.localState(), }, Effect.gen(function* () { const project = yield* Zerops.Project("Project", { mode: "LIGHT" }); const db = yield* Zerops.Postgres("Database", { project, hostname: "db", version: "16", }); const api = yield* Zerops.Service("Api", { project, hostname: "api", type: "nodejs@22", source: { dir: "./api" }, env: { DATABASE_URL: Zerops.ref(db, "connectionString") }, subdomain: true, }); const route = yield* Zerops.HttpRoute("ApiRoute", { project, domains: ["api.example.com"], ssl: false, locations: [{ path: "/", service: api, port: 3000 }], }); return { subdomainUrls: api.subdomainUrls, routeId: route.routeId }; })); ``` - `api/` holds the Node.js app with `package.json`, a lockfile, and `npm start` listening on port 3000. - `api/zerops.yaml` defines setup `api` with a Node.js 22 build and runtime and HTTP port 3000. See the [zerops.yaml specification](/zerops-yaml/specification). - `Zerops.ref(db, "connectionString")` renders a server-side environment reference, so no database credentials are fetched. - Configure custom-domain DNS separately. Certificate issuance is not awaited. ```sh bunx alchemy plan bunx alchemy deploy ``` ## Resource Map | Alchemy resource | Zerops object | | --- | --- | | `Zerops.Project` | Project | | `Zerops.Service` | Runtime service and source deployment; optional generated subdomain | | `Zerops.ManagedService` | Managed service without application code | | Managed family helpers | Typed managed services; see below | | `Zerops.ProjectVariable` | Project environment variable | | `Zerops.ServiceVariable` | Service environment variable | | `Zerops.ObjectStorage` | Object-storage service, quota, access policy | | `Zerops.HttpRoute` | Custom-domain HTTP routing | | `Zerops.PortRoute` | Public IPv6 TCP/UDP routing | ## Managed Families | Helper | Versions | Modes | Floor | Typical ref keys | | --- | --- | --- | --- | --- | | `Postgres` | 14, 16, 17, 18 | Single / HA | Disk 2 GB | `connectionString`, `hostname`, `user`, `password` | | `MariaDB` | 10.6 | Single / HA | Catalog minimum | `connectionString`, `dbName`, `user`, `password` | | `Valkey` | 7.2 | Single / HA | Catalog minimum | `connectionString`, `connectionTlsString`, `hostname`, `password` | | `ClickHouse` | 25.3 | Single / HA | RAM 0.375 GB | `connectionString`, `portHttp`, `user`, `password` | | `Elasticsearch` | 8.16, 9.2 | Single / HA | RAM 2 GB | `connectionString`, `hostname`, `user`, `password` | | `Kafka` | 3.9 | Single / HA | Catalog minimum | `hostname`, `port`, `user`, `password` | | `Nats` | 2.10, 2.12 | Single / HA | Catalog minimum | `connectionString`, `portManagement`, `user`, `password` | | `Qdrant` | 1.10, 1.12 | Single / HA | Catalog minimum | `connectionString`, `grpcConnectionString`, `apiKey`, `readOnlyApiKey` | | `Typesense` | 27.1, 30.2 | Single / HA | Catalog minimum | `connectionString`, `apiKey`, `hostname`, `port` | | `SeaweedFS` | 3.85 | Single / HA | Catalog minimum | `hostname`, `port`, `projectId`, `serviceId` | | `Meilisearch` | 1.10, 1.20, 1.44 | Single only | Catalog minimum | `connectionString`, `masterKey`, `defaultSearchKey`, `hostname` | | `LocalStorage` | 1 | Single only | Catalog minimum | `hostname`, `projectId`, `serviceId`, `BACKUP_PERIOD` | - Helpers take `project`, `version`, an optional `hostname`, and managed-service props. The default mode is `"NON_HA"` (single). - `mode: "HA"` unlocks PostgreSQL `connectionStringReplicas` / `portReplicas` and Valkey `connectionStringReplicas` / `connectionTlsStringReplicas` / `portReplicas` / `portTlsReplicas`. - `SeaweedFS` is shared storage, and deprecated `shared-storage` import aliases map to it. - `ManagedService({ type })` is the untyped escape hatch. Family floors and mode restrictions still apply. - For full ref keys and floor environment overrides, see the [provider README](https://github.com/zeropsio/alchemy#managed-families). ## Deploy Sources | Source | `Zerops.Service` props | | --- | --- | | Directory | `source: { dir: "./api" }` | | Archive | `source: { archive: "./api.tar.gz" }`; `build: false` for prebuilt code | | Git ref | `source: { git: { url: "https://github.com/owner/repo.git", ref: "v1.0.0" } }` | - Use exactly one source mode per deployment. - Root `zerops.yml` / `zerops.yaml`, or explicit `zeropsYaml`; `setup` defaults to the service hostname. - Git branches and tags resolve to commits. Tags and full 40-character SHAs are fetched provider-side, not as branch-HEAD builds on Zerops. - For private HTTP(S) Git repositories, `git.auth` accepts an Effect `Redacted` token. Do not put credentials in URLs. - Directory and Git sources exclude `.git` and `.alchemy`. Use `.deployignore` or the directory `ignore` option for other sensitive files. ## Adoption and Ownership - To adopt existing unowned resources, run `bunx alchemy deploy --adopt`. Subsequent updates and deletion then belong to the stack. - The project tag `alchemy:<48 hex>` hashes the stack, stage, and resource FQN. Matching tags recover ownership after state loss without `--adopt`. - Keep logical IDs, stack, and stage stable, and do not remove or forge ownership tags. - Existing explicit service and storage hostnames, variables, and routes require adoption. Incompatible service types are refused. - Resource `.ref(...)` reads Alchemy state. It is not a remote lookup or adoption. ## Behavior Notes - Check drift with `bunx alchemy drift --no-input`. To repair drift intentionally, use `bunx alchemy drift --repair --no-input`. - Generated secrets stay out of resource attributes, and environment attributes store digests. Masked secret-content drift cannot be detected. - Resolved props, including user-supplied secrets, persist as plaintext in Alchemy state. `Redacted` does not encrypt state. - Routing sync commits all staged edits for the project and routing kind. Foreign staged edits block sync unless `forceSync: true` or `ZEROPS_ROUTING_FORCE_SYNC=true` is set. Use one routing writer per project and routing kind. - Unset scaling minima default to family floors, and explicit min/max values below the floor are rejected. `ZEROPS_POSTGRESQL_MIN_DISK_GB`, `ZEROPS_CLICKHOUSE_MIN_RAM_GB`, and `ZEROPS_ELASTICSEARCH_MIN_RAM_GB` replace their floors, bounded below by catalog minimums (1 GB disk, 0.25 GB RAM, and 0.75 GB RAM respectively). - Alchemy v2 is in beta, and the provider is pinned to `alchemy@2.0.0-beta.84`. - Destroy deletes managed resources, including database and object-storage data. ## Links - [GitHub repository](https://github.com/zeropsio/alchemy) - [Alchemy documentation](https://alchemy.run)