Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions apps/docs/content/references/alchemy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
title: Alchemy
description: Manage Zerops resources and deployments with Alchemy.
---

Provider version: **0.2.0**; requires `alchemy@2.0.0-beta.84`.

## 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; default `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: `token`, optional `endpoint`, optional `organizationId`.

## Minimal stack

```ts title="alchemy.run.ts"
import * as Alchemy from "alchemy";
import * as Effect from "effect/Effect";
import * as Zerops from "@zerops/alchemy";

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/`: Node.js app with `package.json`, lockfile, and `npm start` listening on port 3000.
- `api/zerops.yml`: setup `api`, Node.js 22 build/run, HTTP port 3000;
see [zerops.yml specification](/zerops-yaml/specification).
- `Zerops.ref(db, "connectionString")` renders a server-side environment reference;
no database credentials 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 | Catalogue minimum | `connectionString`, `dbName`, `user`, `password` |
| `Valkey` | 7.2 | Single / HA | Catalogue 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 | Catalogue minimum | `hostname`, `port`, `user`, `password` |
| `Nats` | 2.10, 2.12 | Single / HA | Catalogue minimum | `connectionString`, `portManagement`, `user`, `password` |
| `Qdrant` | 1.10, 1.12 | Single / HA | Catalogue minimum | `connectionString`, `grpcConnectionString`, `apiKey`, `readOnlyApiKey` |
| `Typesense` | 27.1, 30.2 | Single / HA | Catalogue minimum | `connectionString`, `apiKey`, `hostname`, `port` |
| `SeaweedFS` | 3.85 | Single / HA | Catalogue minimum | `hostname`, `port`, `projectId`, `serviceId` |
| `Meilisearch` | 1.10, 1.20, 1.44 | Single only | Catalogue minimum | `connectionString`, `masterKey`, `defaultSearchKey`, `hostname` |
| `LocalStorage` | 1 | Single only | Catalogue minimum | `hostname`, `projectId`, `serviceId`, `BACKUP_PERIOD` |

- Helpers take `project`, `version`, optional `hostname` and managed-service props; default mode is `"NON_HA"` (single).
- `mode: "HA"` unlocks PostgreSQL `connectionStringReplicas` / `portReplicas` and Valkey `connectionStringReplicas` / `connectionTlsStringReplicas` / `portReplicas` / `portTlsReplicas`.
- `SeaweedFS` is shared storage; deprecated `shared-storage` import aliases map to it.
- `ManagedService({ type })` is the untyped escape hatch; family floors and mode restrictions still apply.
- Full ref keys and floor environment overrides: [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" } }` |

- 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 branch-HEAD builds on Zerops.
- Private HTTP(S) Git: `git.auth` accepts an Effect `Redacted` token; no credentials in URLs.
- Directory/Git sources exclude `.git` and `.alchemy`; use `.deployignore` or directory `ignore` for other sensitive files.

## Adoption and ownership

- Existing unowned resources: `bunx alchemy deploy --adopt`; subsequent updates and deletion belong to the stack.
- Project tag `alchemy:<48 hex>` hashes stack/stage/resource FQN; matching tags recover ownership after state loss without `--adopt`.
- Keep logical IDs, stack, and stage stable; do not remove or forge ownership tags.
- Existing explicit service/storage hostnames and variables/routes require adoption; incompatible service types are refused.
- Resource `.ref(...)` reads Alchemy state; not remote lookup or adoption.

## Behaviour notes

- Drift: `bunx alchemy drift --no-input`; intentional repair: `bunx alchemy drift --repair --no-input`.
- Generated secrets stay out of resource attributes; 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/routing kind. Foreign staged edits block sync unless `forceSync: true` or `ZEROPS_ROUTING_FORCE_SYNC=true`; use one routing writer per project/kind.
- Unset scaling minima default to family floors; explicit min/max 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 catalogue minima (1 GB disk, 0.25 GB RAM and 0.75 GB RAM respectively).
- Alchemy v2 is beta; provider 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)
9 changes: 9 additions & 0 deletions apps/docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,15 @@ module.exports = {
},
className: 'homepage-sidebar-item',
},
{
type: 'doc',
id: 'references/alchemy',
label: 'Alchemy',
customProps: {
sidebar_icon: 'curly-braces',
},
className: 'homepage-sidebar-item',
},
{
type: 'doc',
id: 'references/api',
Expand Down
Loading