1import type {
2 CertificateCheckResponse,
3 CertificateDetail,
4 CheckAppCertificateError,
5 CreateAppAcmeCertificateError,
6 CreateAppCustomCertificateError,
7 DeleteAppCertificateError,
8 DeleteAppCustomCertificateError,
9 GetAppCertificateError,
10} from "@distilled.cloud/fly-io/machines";
11import type * as Effect from "effect/Effect";
12import type * as Redacted from "effect/Redacted";
13import * as Binding from "../Binding.ts";
14import type { RuntimeContext } from "../RuntimeContext.ts";
15import type { App } from "./App.ts";
16
17/**
18 * Manage an App's TLS certificates at runtime — the companion to the
19 * deploy-time {@link Certificate} resource, for services that mint or
20 * rotate certificates on their own (a relay adding a tenant's wildcard,
21 * a renewal loop).
22 *
23 * The App is fixed by `WriteCertificates(app)`; calls take a hostname.
24 *
25 * ### Fly-managed (Let's Encrypt)
26 * **Example:** Request and check
27 * ```typescript
28 * const certs = yield* Fly.WriteCertificates(Site);
29 * const requested = yield* certs.request("www.example.com");
30 * // publish requested.dns_requirements, then:
31 * const status = yield* certs.check("www.example.com");
32 * ```
33 *
34 * ### Bring your own PEM
35 * A certificate issued elsewhere (an `ACME.IssueCertificate` wildcard
36 * from ZeroSSL, say) goes up as a custom certificate.
37 *
38 * **Example:** Upload
39 * ```typescript
40 * yield* certs.upload({
41 * hostname: "*.tenant.example.com",
42 * fullchain: issued.chain,
43 * privateKey: issued.privateKey,
44 * });
45 * ```
46 *
47 * @binding
48 * @product Certificate
49 */
50export interface WriteCertificates extends Binding.Service<
51 WriteCertificates,
52 "Fly.WriteCertificates",
53 (app: App) => Effect.Effect<WriteCertificatesClient>
54> {}
55
56export const WriteCertificates = Binding.Service<WriteCertificates>(
57 "Fly.WriteCertificates",
58);
59
60export interface UploadCertificateRequest {
61 readonly hostname: string;
62 /** PEM chain, leaf first. */
63 readonly fullchain: string;
64 /** PEM private key. */
65 readonly privateKey: Redacted.Redacted<string> | string;
66}
67
68/** Certificate operations on one App. */
69export interface WriteCertificatesClient {
70 /** Request a Fly-managed (Let's Encrypt) certificate. Idempotent. */
71 request(
72 hostname: string,
73 ): Effect.Effect<
74 CertificateDetail | undefined,
75 CreateAppAcmeCertificateError,
76 RuntimeContext
77 >;
78 /** Upload a custom PEM. Replaces an existing custom certificate. */
79 upload(
80 request: UploadCertificateRequest,
81 ): Effect.Effect<
82 CertificateDetail | undefined,
83 CreateAppCustomCertificateError | DeleteAppCustomCertificateError,
84 RuntimeContext
85 >;
86 /** Re-run Fly's validation checks for the hostname. */
87 check(
88 hostname: string,
89 ): Effect.Effect<
90 CertificateCheckResponse,
91 CheckAppCertificateError,
92 RuntimeContext
93 >;
94 /** The certificate for a hostname, or `undefined`. */
95 get(
96 hostname: string,
97 ): Effect.Effect<
98 CertificateDetail | undefined,
99 GetAppCertificateError,
100 RuntimeContext
101 >;
102 /** Remove the hostname's certificate. Missing counts as removed. */
103 remove(
104 hostname: string,
105 ): Effect.Effect<void, DeleteAppCertificateError, RuntimeContext>;
106}