Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
973 changes: 502 additions & 471 deletions .agents/skills/alchemy/SKILL.md

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions .agents/skills/alchemy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@wyattjoh/alchemy-skill",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "bun scripts/build-skill.ts",
"check": "bun scripts/build-skill.ts --check"
},
"dependencies": {
"effect": "4.0.0",
"yaml": "^2.9.1"
}
}
466 changes: 0 additions & 466 deletions .agents/skills/alchemy/reference/examples.md

This file was deleted.

483 changes: 0 additions & 483 deletions .agents/skills/alchemy/reference/provider-implementation.md

This file was deleted.

100 changes: 0 additions & 100 deletions .agents/skills/alchemy/reference/upstream-concepts.md

This file was deleted.

14 changes: 14 additions & 0 deletions .agents/skills/alchemy/references/acme/INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# acme index

8 pages. Manage TLS certificates independently of your hosting provider, at deploy time or from an application.

| Page | File | Covers |
| --- | --- | --- |
| ACME | `_overview.md` | Manage TLS certificates independently of your hosting provider, at deploy time or from an application. |
| Certificate authorities | `certificate-authorities.md` | Select a CA, configure external account binding, and understand account identity and key storage. |
| DNS validation | `dns-validation.md` | Configure Cloudflare DNS-01 challenges, wildcard certificates, propagation, and custom solvers. |
| Getting started | `getting-started.md` | Issue a staging TLS certificate with Let's Encrypt and an existing Cloudflare DNS zone. |
| Renewal & revocation | `renewal.md` | Schedule renewal, understand replacement and deletion, and protect certificate and account keys. |
| Runtime issuance | `runtime.md` | Deploy an authenticated Worker that issues and revokes certificates using a bound ACME account. |
| Troubleshooting | `troubleshooting.md` | Diagnose failed DNS challenges, rejected orders, rate limits, and runtime certificate operations. |
| Using certificates | `using-certificates.md` | Upload an ACME certificate to Fly or pass its chain and private key to another TLS consumer. |
66 changes: 66 additions & 0 deletions .agents/skills/alchemy/references/acme/_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!-- source: https://alchemy.run/acme
upstream: website/src/content/docs/acme/index.mdx
alchemy 2.0.0-beta.79 @ e354a45 -->

# ACME

> Manage TLS certificates independently of your hosting provider, at deploy time or from an application.

ACME (Automatic Certificate Management Environment) is the protocol used by
certificate authorities such as Let's Encrypt and ZeroSSL to issue TLS
certificates. Alchemy manages the account and certificate separately from the
service that terminates TLS. You can issue a certificate once and pass its PEM
chain and private key to Fly or another consumer.

## Choose when to issue

| | During deployment | From an application |
| --- | --- | --- |
| API | `ACME.Certificate` | `ACME.IssueCertificate` |
| Account | `ACME.Account` | Bind an existing `ACME.Account` |
| DNS-01 validation | Serializable solver descriptor | Runtime DNS client wrapped as a solver |
| Certificate storage | Stack state | Your application's responsibility |
| Renewal | Evaluated on each deployment | Your application's scheduler |
| Revocation | Optional on resource deletion | Explicit `revoke` call |

Start with a deploy-time certificate unless your application needs to issue
certificates without redeploying. Both paths support wildcard names and use
DNS-01 validation; HTTP-01 and TLS-ALPN-01 are not implemented.

## Guides

- [Getting started](/acme/getting-started) — deploy a Let's Encrypt staging
certificate using an existing Cloudflare zone.
- [Certificate authorities](/acme/certificate-authorities) — choose staging or
production, configure ZeroSSL external account binding, and understand account keys.
- [DNS validation](/acme/dns-validation) — configure Cloudflare, validate wildcard
names, tune propagation, and implement a custom solver.
- [Renewal & revocation](/acme/renewal) — schedule deployments, understand
replacement and deletion, and protect persisted keys.
- [Runtime issuance](/acme/runtime) — deploy an authenticated Worker that issues
and revokes certificates, then integrate your own storage and renewal scheduler.
- [Using certificates](/acme/using-certificates) — upload a certificate to Fly
or pass its outputs to another TLS consumer.
- [Troubleshooting](/acme/troubleshooting) — diagnose DNS failures, CA rejection,
rate limits, and runtime connectivity.

## Security and lifecycle

Account and deploy-time certificate private keys are stored in stack state.
`Redacted` masks ordinary logs; it does **not** encrypt your state backend.
Protect the state store, backups, deployment credentials, and any runtime that
receives the account key.

Renewal needs execution: a certificate resource checks its expiry when you deploy,
not in a background process. Runtime-issued certificates are not tracked as stack
resources. In either mode, monitor expiry and arrange renewal before relying on a
certificate in production.

## API reference

The generated references document each property and method:

- [Account](/providers/acme/account)
- [Certificate](/providers/acme/certificate)
- [IssueCertificate](/providers/acme/issuecertificate)
- [IssueCertificateHttp](/providers/acme/issuecertificatehttp)
159 changes: 159 additions & 0 deletions .agents/skills/alchemy/references/acme/certificate-authorities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<!-- source: https://alchemy.run/acme/certificate-authorities
upstream: website/src/content/docs/acme/certificate-authorities.mdx
alchemy 2.0.0-beta.79 @ e354a45 -->

# Certificate authorities

> Select a CA, configure external account binding, and understand account identity and key storage.

An `ACME.Account` is the identity used to sign orders with one certificate
authority (CA). Certificates reference that account; changing the issuing account
replaces the dependent certificate resource.

## Choose a directory

| Preset | Intended use | External account binding |
| --- | --- | --- |
| `ACME.LetsEncryptStaging` | Development; untrusted certificates | No |
| `ACME.LetsEncrypt` | Publicly trusted production certificates | No |
| `ACME.ZeroSSL` | ZeroSSL ACME issuance | Required |
| `ACME.GoogleTrustServicesStaging` | Google Trust Services staging | Required |
| `ACME.GoogleTrustServices` | Google Trust Services production | Required |

A preset selects a directory URL, not an Alchemy subscription. CA eligibility,
issuance limits, validity periods, and terms still apply. Check the CA's current
requirements before deploying. The supplied Google presets do not imply that a
Google account or external binding credentials are provisioned for you.

## Start with staging

Inside the stack from [Getting started](/acme/getting-started):

```typescript
const account = yield* ACME.Account("Issuer", {
ca: ACME.LetsEncryptStaging,
contact: ["mailto:ops@example.com"],
termsOfServiceAgreed: true,
});
```

Staging exercises real account registration and DNS-01 validation. Its certificate
chain is intentionally untrusted; a browser warning is expected even after a
successful deployment.

## Switch to production

```diff lang="typescript"
const account = yield* ACME.Account("Issuer", {
- ca: ACME.LetsEncryptStaging,
+ ca: ACME.LetsEncrypt,
```

Changing the directory replaces the account and reissues dependent certificates.
Use a separate production stage and state store when development certificates
must remain independent. Schedule [renewal](/acme/renewal) before deploying a
production certificate to a live service.

## Configure ZeroSSL external account binding

External account binding (EAB) associates an ACME account with a CA customer
account. Obtain a matching key ID and HMAC key from ZeroSSL and put them in your
secret configuration as `ZEROSSL_EAB_KID` and `ZEROSSL_EAB_HMAC_KEY`.

```typescript
import * as ACME from "alchemy/ACME";
import * as Config from "effect/Config";

export const Issuer = ACME.Account("Issuer", {
ca: ACME.ZeroSSL,
eab: {
keyId: Config.String("ZEROSSL_EAB_KID"),
hmacKey: Config.Redacted("ZEROSSL_EAB_HMAC_KEY"),
},
termsOfServiceAgreed: true,
});
```

The HMAC key is the CA-issued base64url value, not your ZeroSSL REST API key.
Keep the pair together. Yield `Issuer` in the stack or reference it from a
certificate to register it. Google Trust Services uses the same `eab` shape with
credentials obtained from Google.

## Reuse an existing account resource

```typescript
const account = yield* Issuer;
const certificate = yield* ACME.Certificate("Api", {
account,
identifiers: ["api.example.com"],
solver: Cloudflare.DNS.AcmeSolver({
zoneId: Config.String("CLOUDFLARE_ZONE_ID"),
}),
});
```

Referencing the same account resource reuses its persisted signing key and account
URL. EAB is used when registering the account; ordinary signed requests use the
account key. Changing the EAB key ID replaces the account. Changing only the HMAC
value does not force an existing account to register again.

## Choose an account key algorithm

```diff lang="typescript"
export const Issuer = ACME.Account("Issuer", {
ca: ACME.ZeroSSL,
+ keyAlgorithm: "RS256",
```

The default is `ES256`; `RS256` is also supported. Changing the account algorithm
replaces the account. A certificate's separate `keyAlgorithm` controls the key
used by your TLS server, not the account signing key.

## Use a custom directory

```typescript
export const PrivateIssuer = ACME.Account("PrivateIssuer", {
ca: {
directoryUrl: "https://ca.internal.example/acme/acme/directory",
},
termsOfServiceAgreed: true,
});
```

Use this form for another ACME-compatible CA. It must offer DNS-01 challenges for
the requested identifiers. Network access to the directory and authorization
endpoints must be available wherever issuance runs.

## Trust a private CA's HTTPS endpoint

```diff lang="typescript"
ca: {
directoryUrl: "https://ca.internal.example/acme/acme/directory",
+ trustedRoot: Config.String("ACME_CA_ROOT_PEM"),
},
```

`trustedRoot` is a PEM trust anchor for the CA's **API connection**, not the chain
that your application serves. The current HTTP implementation supplies Bun's
`fetch` TLS option; other runtimes may ignore it. Use Bun for this private-root
path, or arrange platform-level trust and verify connectivity in your target
runtime. It does not disable certificate verification.

## Preserve account identity

Alchemy generates the account key and stores it in stack state. `AccountProps`
does not currently accept an existing account key or account URL, so importing an
externally created account is not supported by this resource. The lower-level
Distilled SDK can accept explicit credentials, but it does not import them into
Alchemy's resource lifecycle.

Back up state securely. Recreating a lost state file generates a new key rather
than recovering the old account. A crash after CA registration but before the
first state save can likewise leave an account whose key was not persisted.
`Redacted` protects ordinary logs, not the storage backend.

## Next steps

- [DNS validation](/acme/dns-validation) — prove control of the requested names.
- [Runtime issuance](/acme/runtime) — bind this account into an application.
- [Account reference](/providers/acme/account) — all account props and outputs.
Loading
Loading