@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-EXCEPTION +26 -0
- package/README.md +34 -0
- package/dist/chunk-3R6KEFOI.js +22 -0
- package/dist/chunk-67BDAMYX.js +376 -0
- package/dist/chunk-AITGS27R.js +139 -0
- package/dist/chunk-CMAEHQ4F.js +6 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-O3UIK34B.js +1430 -0
- package/dist/chunk-VHTUSZUX.js +176 -0
- package/dist/cli.js +9006 -0
- package/dist/docker.js +54 -0
- package/dist/index.js +1053 -0
- package/dist/ingress.js +436 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/preact.js +22 -0
- package/dist/service-url.js +11 -0
- package/dist/services.js +213 -0
- package/dist/test.js +5069 -0
- package/dist/ui.js +254 -0
- package/dist/worker-protocol.js +89 -0
- package/package.json +112 -0
- package/src/app-icon.ts +74 -0
- package/src/apply.ts +52 -0
- package/src/bundle-ui.ts +211 -0
- package/src/bundle.ts +546 -0
- package/src/cli/build-dev-landing.ts +84 -0
- package/src/cli/build-install-panel.ts +90 -0
- package/src/cli/build-runtime.ts +133 -0
- package/src/cli/build.ts +63 -0
- package/src/cli/dev.ts +107 -0
- package/src/cli/down.ts +60 -0
- package/src/cli/main.ts +56 -0
- package/src/cli/test.ts +61 -0
- package/src/cli/validate.ts +37 -0
- package/src/compile.ts +100 -0
- package/src/create-app.ts +982 -0
- package/src/deep-merge.ts +33 -0
- package/src/dev/fixture.ts +48 -0
- package/src/dev/install-handler.ts +383 -0
- package/src/dev/server.ts +968 -0
- package/src/dev/stack.ts +108 -0
- package/src/dev/state.ts +159 -0
- package/src/docker.ts +96 -0
- package/src/errors.ts +61 -0
- package/src/factory.ts +481 -0
- package/src/garage.ts +56 -0
- package/src/generator/hash.ts +6 -0
- package/src/generator/resolve-refs.ts +207 -0
- package/src/grants-builder.ts +100 -0
- package/src/grants.ts +260 -0
- package/src/home-api.ts +172 -0
- package/src/home-cookie.ts +10 -0
- package/src/home-registry.ts +120 -0
- package/src/home.ts +187 -0
- package/src/host-registry.ts +143 -0
- package/src/http-header.ts +20 -0
- package/src/identifiers.ts +49 -0
- package/src/index.ts +66 -0
- package/src/info.ts +64 -0
- package/src/ini.ts +50 -0
- package/src/json.ts +14 -0
- package/src/jsx-runtime/index.ts +5 -0
- package/src/keys.ts +29 -0
- package/src/mysql.ts +56 -0
- package/src/pack-build.ts +108 -0
- package/src/pack.ts +115 -0
- package/src/peer-registry.ts +121 -0
- package/src/peer-world.ts +117 -0
- package/src/permission-meta.ts +116 -0
- package/src/permissions.ts +108 -0
- package/src/pkg-root.ts +34 -0
- package/src/postgres.ts +79 -0
- package/src/preact/index.ts +27 -0
- package/src/redis.ts +43 -0
- package/src/ref.ts +271 -0
- package/src/remote-registry.ts +33 -0
- package/src/required-host-kinds.ts +110 -0
- package/src/resolve-context.ts +111 -0
- package/src/resolve.ts +471 -0
- package/src/runtime/docker/compose/compose-schema.ts +118 -0
- package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
- package/src/runtime/docker/compose/default-hardening.ts +21 -0
- package/src/runtime/docker/compose/effects.ts +107 -0
- package/src/runtime/docker/compose/index.ts +17 -0
- package/src/runtime/docker/compose/labels.ts +39 -0
- package/src/runtime/docker/compose/types.ts +78 -0
- package/src/runtime/docker/home-proxy-server/index.ts +177 -0
- package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
- package/src/runtime/docker/ingress/build-config.ts +819 -0
- package/src/runtime/docker/ingress/index.ts +16 -0
- package/src/runtime/docker/ingress/types.ts +182 -0
- package/src/runtime/docker/log-frames.ts +121 -0
- package/src/runtime/docker/logs-server/index.ts +366 -0
- package/src/runtime/docker/spawn-server/context-key.ts +82 -0
- package/src/runtime/docker/spawn-server/index.ts +492 -0
- package/src/runtime/docker/spawn-server/protocol.ts +70 -0
- package/src/runtime/docker/spawn-server/spec.ts +122 -0
- package/src/runtime/docker/stats-server/disks.ts +161 -0
- package/src/runtime/docker/stats-server/index.ts +322 -0
- package/src/runtime/home-client/index.ts +71 -0
- package/src/runtime/log-line.ts +24 -0
- package/src/runtime/logs-client/index.ts +175 -0
- package/src/runtime/spawn-client/index.ts +93 -0
- package/src/runtime/spawn-client/kaniko.ts +79 -0
- package/src/runtime/stats-client/index.ts +65 -0
- package/src/runtime/stats-sample.ts +95 -0
- package/src/safe-json.ts +37 -0
- package/src/sandbox.ts +1209 -0
- package/src/schema/build-apps.ts +38 -0
- package/src/schema/home.ts +217 -0
- package/src/schema/host.ts +792 -0
- package/src/schema/inputs.ts +26 -0
- package/src/schema/nodes.ts +75 -0
- package/src/schema/peer.ts +400 -0
- package/src/schema/public-domain.ts +116 -0
- package/src/schema/remote.ts +74 -0
- package/src/security.ts +594 -0
- package/src/serialize.ts +21 -0
- package/src/service-definition.ts +284 -0
- package/src/service-url.ts +64 -0
- package/src/service.ts +39 -0
- package/src/services/index.ts +5 -0
- package/src/shared.ts +65 -0
- package/src/speculative-silo.ts +96 -0
- package/src/stable-secret.ts +14 -0
- package/src/surfaces.ts +264 -0
- package/src/tailwind-bin.ts +56 -0
- package/src/test/boot.ts +192 -0
- package/src/test/compile.ts +501 -0
- package/src/test/describe.ts +117 -0
- package/src/test/index.ts +11 -0
- package/src/test/ingress-fixture.ts +37 -0
- package/src/test/types.ts +318 -0
- package/src/trusted-headers.ts +79 -0
- package/src/types.ts +126 -0
- package/src/ui/components/aside.tsx +96 -0
- package/src/ui/components/badge.tsx +42 -0
- package/src/ui/components/button.tsx +59 -0
- package/src/ui/components/card.tsx +106 -0
- package/src/ui/components/checkbox.tsx +37 -0
- package/src/ui/components/code-block.tsx +29 -0
- package/src/ui/components/combobox.tsx +119 -0
- package/src/ui/components/config-field.tsx +284 -0
- package/src/ui/components/dialog.tsx +137 -0
- package/src/ui/components/domain-editor.tsx +317 -0
- package/src/ui/components/domain-list.ts +205 -0
- package/src/ui/components/dropdown-menu.tsx +73 -0
- package/src/ui/components/empty-state.tsx +51 -0
- package/src/ui/components/highlight.ts +59 -0
- package/src/ui/components/icons.tsx +101 -0
- package/src/ui/components/image-posture.tsx +85 -0
- package/src/ui/components/input.tsx +28 -0
- package/src/ui/components/label.tsx +16 -0
- package/src/ui/components/line-chart.tsx +113 -0
- package/src/ui/components/page.tsx +39 -0
- package/src/ui/components/permission-row.tsx +262 -0
- package/src/ui/components/permission-section.tsx +51 -0
- package/src/ui/components/pie-chart.tsx +245 -0
- package/src/ui/components/schema-block.tsx +60 -0
- package/src/ui/components/separator.tsx +42 -0
- package/src/ui/components/sheet.tsx +130 -0
- package/src/ui/components/sidebar.tsx +650 -0
- package/src/ui/components/silo-picker.tsx +510 -0
- package/src/ui/components/skeleton.tsx +13 -0
- package/src/ui/components/sonner.tsx +33 -0
- package/src/ui/components/switch.tsx +58 -0
- package/src/ui/components/table-view.ts +219 -0
- package/src/ui/components/table.tsx +506 -0
- package/src/ui/components/tabs.tsx +59 -0
- package/src/ui/components/timeago.tsx +84 -0
- package/src/ui/components/tooltip.tsx +56 -0
- package/src/ui/components.json +21 -0
- package/src/ui/dev/dev-shell.tsx +145 -0
- package/src/ui/home/home-grid.tsx +177 -0
- package/src/ui/hooks/use-mobile.ts +19 -0
- package/src/ui/index.ts +189 -0
- package/src/ui/install/adapter.ts +193 -0
- package/src/ui/install/deploy-targets-picker.tsx +185 -0
- package/src/ui/install/install-app-panel.tsx +912 -0
- package/src/ui/install/install-error.tsx +62 -0
- package/src/ui/install/perms-inactive.ts +50 -0
- package/src/ui/install/required-fields.ts +37 -0
- package/src/ui/install/tabs-form.tsx +557 -0
- package/src/ui/tokens.css +279 -0
- package/src/ui/utils.ts +78 -0
- package/src/ui-error.ts +139 -0
- package/src/ui-runtime/dev-landing-entry.tsx +258 -0
- package/src/ui-runtime/index.ts +83 -0
- package/src/ui-runtime/install-entry.tsx +119 -0
- package/src/ui-runtime/mount.ts +367 -0
- package/src/ui-runtime/runtime.css +56 -0
- package/src/ui-surface.ts +451 -0
- package/src/valkey.ts +43 -0
- package/src/worker/extract-tar.ts +125 -0
- package/src/worker/identity.ts +183 -0
- package/src/worker/index.ts +16 -0
- package/src/worker/public-ip.ts +92 -0
- package/src/worker/queue.ts +54 -0
- package/src/worker/reconcile.ts +127 -0
- package/src/worker/start-worker.ts +227 -0
- package/src/worker/ui-proxy.ts +335 -0
- package/src/worker/uplink.ts +523 -0
- package/src/worker/validate.ts +34 -0
- package/src/worker/verify-relay.ts +102 -0
- package/src/worker-protocol/app-dirs.ts +94 -0
- package/src/worker-protocol/home-proxy-paths.ts +76 -0
- package/src/worker-protocol/index.ts +55 -0
- package/src/worker-protocol/ingress.ts +166 -0
- package/src/worker-protocol/logs-socket-path.ts +50 -0
- package/src/worker-protocol/networks.ts +39 -0
- package/src/worker-protocol/node-version.ts +70 -0
- package/src/worker-protocol/silo-paths.ts +114 -0
- package/src/worker-protocol/spawn-paths.ts +165 -0
- package/src/worker-protocol/stats-socket-path.ts +40 -0
- package/src/worker-protocol/update-files.ts +77 -0
- package/src/worker-protocol/uplink/constants.ts +86 -0
- package/src/worker-protocol/uplink/errors.ts +61 -0
- package/src/worker-protocol/uplink/frame.ts +114 -0
- package/src/worker-protocol/uplink/identity.ts +40 -0
- package/src/worker-protocol/uplink/index.ts +63 -0
- package/src/worker-protocol/uplink/methods.ts +462 -0
- package/src/worker-protocol/uplink/mux.ts +646 -0
- package/src/worker-protocol/uplink/protocol.ts +11 -0
- package/src/worker-protocol/uplink/rpc.ts +309 -0
- package/src/worker-protocol/uplink/session.ts +540 -0
- package/src/worker-protocol/uplink/test-support.ts +188 -0
- package/src/worker-protocol/worker-mount.ts +27 -0
- package/src/worker-protocol/worker-state.ts +190 -0
- package/src/xml.ts +49 -0
- package/src/yaml.ts +15 -0
package/dist/ingress.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOME_COOKIE_PREFIX
|
|
3
|
+
} from "./chunk-CMAEHQ4F.js";
|
|
4
|
+
import {
|
|
5
|
+
structuredHost
|
|
6
|
+
} from "./chunk-3R6KEFOI.js";
|
|
7
|
+
import "./chunk-MLKGABMK.js";
|
|
8
|
+
|
|
9
|
+
// src/worker-protocol/uplink/constants.ts
|
|
10
|
+
var MAX_PAYLOAD_BYTES = 16 * 1024;
|
|
11
|
+
var DEFAULT_LIMITS = {
|
|
12
|
+
fragmentSize: 16 * 1024,
|
|
13
|
+
initialWindow: 256 * 1024,
|
|
14
|
+
maxStreams: 64,
|
|
15
|
+
maxInFlight: 8 * 1024 * 1024
|
|
16
|
+
};
|
|
17
|
+
var UPLINK_PROLOGUE = Uint8Array.from(
|
|
18
|
+
"realm-uplink/1",
|
|
19
|
+
(c) => c.charCodeAt(0)
|
|
20
|
+
);
|
|
21
|
+
var SOCKET_HIGH_WATER = 256 * 1024;
|
|
22
|
+
var SOCKET_INBOUND_CAP = 16 * 1024 * 1024;
|
|
23
|
+
var REKEY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
24
|
+
var REKEY_JITTER_MS = 2 * 60 * 60 * 1e3;
|
|
25
|
+
|
|
26
|
+
// src/runtime/docker/ingress/build-config.ts
|
|
27
|
+
var VERIFY_UPSTREAM = "realm-worker:6021";
|
|
28
|
+
var VERIFY_PATH = "/api/access/verify";
|
|
29
|
+
var REALM_HEADER_WILDCARD = "X-Realm-*";
|
|
30
|
+
var HOME_COOKIE_REGEX = `(__Secure-)?${HOME_COOKIE_PREFIX}\\.[^=]+=[^;]*;?\\s*`;
|
|
31
|
+
var HOME_SET_COOKIE_REGEX = `^(__Secure-)?${HOME_COOKIE_PREFIX}\\.[^=]*=.*$`;
|
|
32
|
+
function holdingPage(title, headline, detail) {
|
|
33
|
+
return [
|
|
34
|
+
`<!doctype html>`,
|
|
35
|
+
`<html lang="en" style="color-scheme: light dark">`,
|
|
36
|
+
`<meta charset="utf-8">`,
|
|
37
|
+
`<meta name="viewport" content="width=device-width,initial-scale=1">`,
|
|
38
|
+
`<meta http-equiv="refresh" content="5">`,
|
|
39
|
+
`<title>${title}</title>`,
|
|
40
|
+
`<body style="background: Canvas; color: CanvasText; font-family: system-ui, sans-serif; display: grid; place-items: center; min-height: 100vh; margin: 0; padding: 2rem; box-sizing: border-box; text-align: center">`,
|
|
41
|
+
`<main style="max-width: 32rem">`,
|
|
42
|
+
`<h1 style="font-size: 1.25rem; font-weight: 600; margin: 0 0 0.75rem">${headline}</h1>`,
|
|
43
|
+
`<p style="margin: 0; opacity: 0.7; line-height: 1.5">${detail}</p>`,
|
|
44
|
+
`</main>`
|
|
45
|
+
].join("");
|
|
46
|
+
}
|
|
47
|
+
var DEPLOYING_PAGE = holdingPage(
|
|
48
|
+
"Starting up",
|
|
49
|
+
"This app is starting up",
|
|
50
|
+
"Its container is not answering yet. This page reloads on its own."
|
|
51
|
+
);
|
|
52
|
+
var AUTH_OK_VAR = "realm_auth_ok";
|
|
53
|
+
var HOME_UNREACHABLE_PAGE = holdingPage(
|
|
54
|
+
"Realm is unreachable",
|
|
55
|
+
"Realm's control plane is not responding",
|
|
56
|
+
"Apps cannot be reached until it is back, even the ones still running."
|
|
57
|
+
);
|
|
58
|
+
var NOT_ROUTED_PAGE = holdingPage(
|
|
59
|
+
"Nothing here",
|
|
60
|
+
"Nothing is serving this address",
|
|
61
|
+
"No app on this worker answers to this name."
|
|
62
|
+
);
|
|
63
|
+
var ASSET_LINKS_PATH = "/.well-known/assetlinks.json";
|
|
64
|
+
var COMPANION_PACKAGE = "eu.yourrealm.companion";
|
|
65
|
+
function assetLinksBody(fingerprints) {
|
|
66
|
+
return JSON.stringify([
|
|
67
|
+
{
|
|
68
|
+
relation: ["delegate_permission/common.handle_all_urls"],
|
|
69
|
+
target: {
|
|
70
|
+
namespace: "android_app",
|
|
71
|
+
package_name: COMPANION_PACKAGE,
|
|
72
|
+
sha256_cert_fingerprints: fingerprints
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
var DEFAULT_SECURITY_HEADERS = {
|
|
78
|
+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
|
79
|
+
"X-Frame-Options": "SAMEORIGIN",
|
|
80
|
+
"X-Content-Type-Options": "nosniff",
|
|
81
|
+
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
82
|
+
"Permissions-Policy": "camera=(), microphone=(), geolocation=()"
|
|
83
|
+
};
|
|
84
|
+
function securityHeaderLists() {
|
|
85
|
+
return Object.fromEntries(Object.entries(DEFAULT_SECURITY_HEADERS).map(([k, v]) => [k, [v]]));
|
|
86
|
+
}
|
|
87
|
+
var ACME_CA_ROOT_PATH = "/etc/caddy/acme-ca-root.pem";
|
|
88
|
+
var STREAM_CLOSE_DELAY = `${Math.ceil((REKEY_INTERVAL_MS + REKEY_JITTER_MS) / 36e5)}h`;
|
|
89
|
+
function buildCaddyConfig(input) {
|
|
90
|
+
const { routedApps, dashboardHost, workerId, baseDomain, acmeDns, publicProviders } = input;
|
|
91
|
+
const { cdnProviders, companionFingerprints } = input;
|
|
92
|
+
const routes = [];
|
|
93
|
+
const clientIp = cdnProviders.length > 0 ? "{http.vars.client_ip}" : "{http.request.remote.host}";
|
|
94
|
+
routes.push({
|
|
95
|
+
match: [{ path: [ASSET_LINKS_PATH] }],
|
|
96
|
+
handle: [
|
|
97
|
+
{
|
|
98
|
+
handler: "static_response",
|
|
99
|
+
status_code: 200,
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": ["application/json"],
|
|
102
|
+
...securityHeaderLists()
|
|
103
|
+
},
|
|
104
|
+
body: assetLinksBody(companionFingerprints)
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
terminal: true
|
|
108
|
+
});
|
|
109
|
+
if (dashboardHost !== null) {
|
|
110
|
+
routes.push({
|
|
111
|
+
match: [{ host: [dashboardHost] }],
|
|
112
|
+
handle: [homeProxy("realm-home:3100", clientIp)],
|
|
113
|
+
terminal: true
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
for (const app of routedApps) {
|
|
117
|
+
for (const svc of app.services) {
|
|
118
|
+
const router = svc.router;
|
|
119
|
+
const upstream = `${app.appId}_${svc.serviceId}:${router.containerPort}`;
|
|
120
|
+
const host = structuredHost(app.appId, svc.serviceId, workerId, baseDomain);
|
|
121
|
+
routes.push(buildAuthRoute(host, router, upstream, app.trustedHeaderKeys ?? [], clientIp));
|
|
122
|
+
if (router.publicDomain) {
|
|
123
|
+
const domains = Array.isArray(router.publicDomain) ? router.publicDomain.map(String) : [String(router.publicDomain)];
|
|
124
|
+
pushRoutes(routes, domains, router, upstream, app.trustedHeaderKeys ?? [], clientIp);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (workerId && baseDomain) {
|
|
129
|
+
routes.push({
|
|
130
|
+
match: [{ host: [`*.${workerId}.${baseDomain}`] }],
|
|
131
|
+
handle: [holdingResponse(NOT_ROUTED_PAGE)],
|
|
132
|
+
terminal: true
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const policies = [];
|
|
136
|
+
const automate = [];
|
|
137
|
+
if (acmeDns != null && workerId) {
|
|
138
|
+
const wildcard = `*.${workerId}.${acmeDns.baseDomain}`;
|
|
139
|
+
automate.push(wildcard);
|
|
140
|
+
policies.push({ subjects: [wildcard], issuers: [buildAcmeDnsIssuer(workerId, acmeDns)] });
|
|
141
|
+
}
|
|
142
|
+
if (publicProviders.length > 0) {
|
|
143
|
+
policies.push({ issuers: publicProviders.map(buildPublicIssuer) });
|
|
144
|
+
} else {
|
|
145
|
+
policies.push({ issuers: [{ module: "internal" }] });
|
|
146
|
+
}
|
|
147
|
+
const dynamicDns = acmeDns != null && workerId ? {
|
|
148
|
+
domains: { [acmeDns.baseDomain]: [workerId] },
|
|
149
|
+
dns_provider: {
|
|
150
|
+
name: "realm",
|
|
151
|
+
server_url: acmeDns.serverUrl,
|
|
152
|
+
username: acmeDns.username,
|
|
153
|
+
api_key: acmeDns.apiKey
|
|
154
|
+
},
|
|
155
|
+
ip_sources: [{ source: "simple_http", endpoints: [`${acmeDns.serverUrl}/ip`] }],
|
|
156
|
+
check_interval: "5m",
|
|
157
|
+
versions: { ipv4: true, ipv6: false }
|
|
158
|
+
} : void 0;
|
|
159
|
+
return {
|
|
160
|
+
admin: { listen: "127.0.0.1:2019" },
|
|
161
|
+
apps: {
|
|
162
|
+
http: {
|
|
163
|
+
servers: {
|
|
164
|
+
main: {
|
|
165
|
+
listen: [":443", ":80"],
|
|
166
|
+
routes,
|
|
167
|
+
// With a CDN declared in front, resolve `{client_ip}`
|
|
168
|
+
// right-to-left (strict) past its edge addresses from
|
|
169
|
+
// X-Forwarded-For. Omitted entirely otherwise: Caddy then ignores
|
|
170
|
+
// inbound XFF and uses the direct peer, so a client can't spoof
|
|
171
|
+
// its address when nothing is in front. The module fails soft - an
|
|
172
|
+
// unreachable provider yields no ranges, degrading to that same
|
|
173
|
+
// direct-peer behaviour rather than trusting anything.
|
|
174
|
+
...cdnProviders.length > 0 && {
|
|
175
|
+
trusted_proxies: { source: "cdn", providers: [...cdnProviders] },
|
|
176
|
+
trusted_proxies_strict: 1,
|
|
177
|
+
client_ip_headers: ["X-Forwarded-For"]
|
|
178
|
+
},
|
|
179
|
+
...workerId && baseDomain && {
|
|
180
|
+
errors: {
|
|
181
|
+
// Ordered pair, splitting on which dial failed. The marker is
|
|
182
|
+
// set only once Home has answered and approved, so its absence
|
|
183
|
+
// means Home itself was unreachable. Every app route under
|
|
184
|
+
// this wildcard carries a forward_auth hop (checkPublicDomains
|
|
185
|
+
// refuses a granted publicDomain under baseDomain at eval
|
|
186
|
+
// time), while the dashboard route has none and proxies
|
|
187
|
+
// straight to Home, so it falls to the second page.
|
|
188
|
+
//
|
|
189
|
+
// The first page is true on every box: the marker proves Home
|
|
190
|
+
// answered, so the dial that failed was the app's. The second
|
|
191
|
+
// names Home as the thing that is down, a claim only the box
|
|
192
|
+
// running it can make - the same gate as the dashboard route.
|
|
193
|
+
// A worker-only box leaves an unmarked 502 to Caddy's default.
|
|
194
|
+
routes: [
|
|
195
|
+
buildFallbackRoute(
|
|
196
|
+
workerId,
|
|
197
|
+
baseDomain,
|
|
198
|
+
` && {http.vars.${AUTH_OK_VAR}} == "1"`,
|
|
199
|
+
DEPLOYING_PAGE
|
|
200
|
+
),
|
|
201
|
+
...dashboardHost !== null ? [buildFallbackRoute(workerId, baseDomain, "", HOME_UNREACHABLE_PAGE)] : []
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
tls: {
|
|
209
|
+
...automate.length > 0 && { certificates: { automate } },
|
|
210
|
+
automation: { policies }
|
|
211
|
+
},
|
|
212
|
+
...dynamicDns && { dynamic_dns: dynamicDns }
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function buildAcmeDnsIssuer(subdomain, acme) {
|
|
217
|
+
return {
|
|
218
|
+
module: "acme",
|
|
219
|
+
// Default ACME endpoint (Let's Encrypt prod) unless overridden to a
|
|
220
|
+
// staging endpoint or a test CA (Pebble) in the e2e.
|
|
221
|
+
...acme.ca && { ca: acme.ca },
|
|
222
|
+
...acme.caRootPem && { trusted_roots_pem_files: [ACME_CA_ROOT_PATH] },
|
|
223
|
+
challenges: {
|
|
224
|
+
dns: {
|
|
225
|
+
provider: {
|
|
226
|
+
name: "acmedns",
|
|
227
|
+
server_url: acme.serverUrl,
|
|
228
|
+
username: acme.username,
|
|
229
|
+
password: acme.apiKey,
|
|
230
|
+
// Static at config-gen time: the worker's own label under acme-dns.
|
|
231
|
+
subdomain
|
|
232
|
+
},
|
|
233
|
+
// Wait for the Hetzner write to propagate before asking LE to
|
|
234
|
+
// validate. Too short and LE's multi-perspective validation races
|
|
235
|
+
// propagation: it queries an anycast PoP still serving the previous
|
|
236
|
+
// challenge value and fails with "Incorrect TXT record". Convergence
|
|
237
|
+
// measures ~30s from one vantage point and LE checks worldwide, so the
|
|
238
|
+
// delay sits above that with margin. Polling can't substitute — Caddy
|
|
239
|
+
// sees only its own resolver, never the stale PoP LE hits.
|
|
240
|
+
propagation_delay: "60s"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function buildPublicIssuer(provider) {
|
|
246
|
+
const dnsProvider = provider.name === "cloudflare" ? { name: "cloudflare", api_token: provider.apiToken } : { name: "hetzner", api_token: provider.apiToken };
|
|
247
|
+
return { module: "acme", challenges: { dns: { provider: dnsProvider } } };
|
|
248
|
+
}
|
|
249
|
+
function proxy(upstream, clientIp, response) {
|
|
250
|
+
return {
|
|
251
|
+
handler: "reverse_proxy",
|
|
252
|
+
upstreams: [{ dial: upstream }],
|
|
253
|
+
headers: {
|
|
254
|
+
request: {
|
|
255
|
+
set: {
|
|
256
|
+
"X-Forwarded-For": [clientIp],
|
|
257
|
+
"X-Real-IP": [clientIp]
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
...response ? { response } : {}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function appProxy(upstream, clientIp) {
|
|
265
|
+
return proxy(upstream, clientIp, {
|
|
266
|
+
replace: { "Set-Cookie": [{ search_regexp: HOME_SET_COOKIE_REGEX, replace: "" }] }
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function homeProxy(upstream, clientIp) {
|
|
270
|
+
return { ...proxy(upstream, clientIp), stream_close_delay: STREAM_CLOSE_DELAY };
|
|
271
|
+
}
|
|
272
|
+
function pushRoutes(routes, domains, router, upstream, trustedHeaderKeys, clientIp) {
|
|
273
|
+
if (domains.length === 0) return;
|
|
274
|
+
if (domains.length === 1 || router.domainMode === "all") {
|
|
275
|
+
routes.push(buildPrimaryRoute(domains, router, upstream, trustedHeaderKeys, clientIp));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const [primary, ...rest] = domains;
|
|
279
|
+
if (!primary) return;
|
|
280
|
+
routes.push(buildPrimaryRoute([primary], router, upstream, trustedHeaderKeys, clientIp));
|
|
281
|
+
routes.push(buildRedirectRoute(rest, primary, router.pathPrefix));
|
|
282
|
+
}
|
|
283
|
+
function buildPrimaryRoute(domains, router, upstream, trustedHeaderKeys, clientIp) {
|
|
284
|
+
const handle = [];
|
|
285
|
+
const rateLimitHandler = buildRateLimitHandler(router.rateLimit, clientIp);
|
|
286
|
+
if (rateLimitHandler) handle.push(rateLimitHandler);
|
|
287
|
+
handle.push({
|
|
288
|
+
handler: "headers",
|
|
289
|
+
request: { delete: [REALM_HEADER_WILDCARD, ...trustedHeaderKeys] }
|
|
290
|
+
});
|
|
291
|
+
handle.push(...buildContentHandlers(router));
|
|
292
|
+
handle.push(appProxy(upstream, clientIp));
|
|
293
|
+
return { match: [buildMatcher(domains, router.pathPrefix)], handle, terminal: true };
|
|
294
|
+
}
|
|
295
|
+
function buildAuthRoute(host, router, upstream, trustedHeaderKeys, clientIp) {
|
|
296
|
+
const handle = [];
|
|
297
|
+
const rateLimitHandler = buildRateLimitHandler(router.rateLimit, clientIp);
|
|
298
|
+
if (rateLimitHandler) handle.push(rateLimitHandler);
|
|
299
|
+
handle.push({
|
|
300
|
+
handler: "headers",
|
|
301
|
+
request: { delete: [REALM_HEADER_WILDCARD, ...trustedHeaderKeys] }
|
|
302
|
+
});
|
|
303
|
+
handle.push(...buildForwardAuthHandlers(VERIFY_UPSTREAM, VERIFY_PATH, trustedHeaderKeys));
|
|
304
|
+
handle.push(...buildContentHandlers(router));
|
|
305
|
+
handle.push({
|
|
306
|
+
handler: "headers",
|
|
307
|
+
request: { replace: { Cookie: [{ search_regexp: HOME_COOKIE_REGEX, replace: "" }] } }
|
|
308
|
+
});
|
|
309
|
+
handle.push(appProxy(upstream, clientIp));
|
|
310
|
+
return { match: [buildMatcher([host], router.pathPrefix)], handle, terminal: true };
|
|
311
|
+
}
|
|
312
|
+
function buildContentHandlers(router) {
|
|
313
|
+
const handle = [];
|
|
314
|
+
if (router.compress) {
|
|
315
|
+
handle.push({ handler: "encode", encodings: { gzip: {} }, prefer: ["gzip"] });
|
|
316
|
+
}
|
|
317
|
+
const headerHandler = buildHeaderHandler(router.headers);
|
|
318
|
+
if (headerHandler) handle.push(headerHandler);
|
|
319
|
+
if (router.maxRequestSizeMb != null) {
|
|
320
|
+
handle.push({
|
|
321
|
+
handler: "request_body",
|
|
322
|
+
max_size: router.maxRequestSizeMb * 1024 * 1024
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return handle;
|
|
326
|
+
}
|
|
327
|
+
function buildForwardAuthHandlers(verifyUpstream, verifyPath, trustedHeaderKeys) {
|
|
328
|
+
const onAuthed = [{ handle: [{ handler: "vars", [AUTH_OK_VAR]: "1" }] }];
|
|
329
|
+
for (const k of trustedHeaderKeys) {
|
|
330
|
+
const placeholder = `{http.reverse_proxy.header.${k}}`;
|
|
331
|
+
onAuthed.push({
|
|
332
|
+
match: [{ not: [{ vars: { [placeholder]: [""] } }] }],
|
|
333
|
+
handle: [{ handler: "headers", request: { set: { [k]: [placeholder] } } }]
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return [
|
|
337
|
+
{
|
|
338
|
+
handler: "reverse_proxy",
|
|
339
|
+
rewrite: { method: "GET", uri: verifyPath },
|
|
340
|
+
headers: {
|
|
341
|
+
request: {
|
|
342
|
+
// Host is pinned, not forwarded. Home picks the anonymous gate and
|
|
343
|
+
// the brokered-header mapping from this header alone, and Caddy
|
|
344
|
+
// keeps a client-supplied value once server-level `trusted_proxies`
|
|
345
|
+
// is configured (a declared `cdn`) — which would let a caller borrow
|
|
346
|
+
// another app's gate. `set` is overwrite, so the route's host wins.
|
|
347
|
+
set: {
|
|
348
|
+
"X-Forwarded-Host": ["{http.request.host}"],
|
|
349
|
+
"X-Forwarded-Method": ["{http.request.method}"],
|
|
350
|
+
"X-Forwarded-Uri": ["{http.request.uri}"]
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
// `[2]` is Caddy's shorthand for the whole 2xx class.
|
|
355
|
+
handle_response: [{ match: { status_code: [2] }, routes: onAuthed }],
|
|
356
|
+
upstreams: [{ dial: verifyUpstream }]
|
|
357
|
+
}
|
|
358
|
+
];
|
|
359
|
+
}
|
|
360
|
+
function buildRateLimitHandler(rateLimit, clientIp) {
|
|
361
|
+
if (rateLimit == null) return void 0;
|
|
362
|
+
const { max_events, window } = typeof rateLimit === "number" ? { max_events: rateLimit, window: "1s" } : { max_events: rateLimit.requests, window: rateLimit.window };
|
|
363
|
+
return {
|
|
364
|
+
handler: "rate_limit",
|
|
365
|
+
rate_limits: {
|
|
366
|
+
// `clientIp` must be a placeholder Caddy actually expands — an
|
|
367
|
+
// unresolved literal would collapse every client into one bucket.
|
|
368
|
+
per_ip: { key: clientIp, window, max_events }
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function holdingResponse(body) {
|
|
373
|
+
return {
|
|
374
|
+
handler: "static_response",
|
|
375
|
+
status_code: 503,
|
|
376
|
+
headers: {
|
|
377
|
+
"Content-Type": ["text/html; charset=utf-8"],
|
|
378
|
+
"Retry-After": ["5"],
|
|
379
|
+
...securityHeaderLists()
|
|
380
|
+
},
|
|
381
|
+
body
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function buildFallbackRoute(workerId, baseDomain, extraExpr, body) {
|
|
385
|
+
return {
|
|
386
|
+
match: [
|
|
387
|
+
{
|
|
388
|
+
host: [`*.${workerId}.${baseDomain}`],
|
|
389
|
+
expression: `{http.error.status_code} in [502, 504]${extraExpr}`
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
handle: [holdingResponse(body)],
|
|
393
|
+
terminal: true
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function buildRedirectRoute(domains, primary, pathPrefix) {
|
|
397
|
+
return {
|
|
398
|
+
match: [buildMatcher(domains, pathPrefix)],
|
|
399
|
+
handle: [
|
|
400
|
+
{
|
|
401
|
+
handler: "static_response",
|
|
402
|
+
headers: { Location: [`https://${primary}{http.request.orig_uri}`] },
|
|
403
|
+
status_code: 301
|
|
404
|
+
}
|
|
405
|
+
],
|
|
406
|
+
terminal: true
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function buildMatcher(domains, pathPrefix) {
|
|
410
|
+
return { host: domains, ...pathPrefix ? { path: [`${pathPrefix}*`] } : {} };
|
|
411
|
+
}
|
|
412
|
+
function buildHeaderHandler(overrides) {
|
|
413
|
+
const set = { ...DEFAULT_SECURITY_HEADERS };
|
|
414
|
+
const del = [];
|
|
415
|
+
if (overrides) {
|
|
416
|
+
for (const [k, v] of Object.entries(overrides).sort(([a], [b]) => a < b ? -1 : 1)) {
|
|
417
|
+
if (v === null) {
|
|
418
|
+
delete set[k];
|
|
419
|
+
del.push(k);
|
|
420
|
+
} else {
|
|
421
|
+
set[k] = v;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (Object.keys(set).length === 0 && del.length === 0) return void 0;
|
|
426
|
+
const setArr = {};
|
|
427
|
+
for (const [k, v] of Object.entries(set)) setArr[k] = [v];
|
|
428
|
+
const response = {};
|
|
429
|
+
if (Object.keys(setArr).length > 0) response.set = setArr;
|
|
430
|
+
if (del.length > 0) response.delete = del;
|
|
431
|
+
return { handler: "headers", response };
|
|
432
|
+
}
|
|
433
|
+
export {
|
|
434
|
+
ACME_CA_ROOT_PATH,
|
|
435
|
+
buildCaddyConfig
|
|
436
|
+
};
|
package/dist/preact.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "./chunk-MLKGABMK.js";
|
|
2
|
+
|
|
3
|
+
// src/preact/index.ts
|
|
4
|
+
var stub = (() => {
|
|
5
|
+
throw new Error(
|
|
6
|
+
"@yourrealm/sdk/preact has no runtime in this environment. Hook calls are bundled by realm-sdk and execute inside the Home iframe."
|
|
7
|
+
);
|
|
8
|
+
});
|
|
9
|
+
var useCallback = stub;
|
|
10
|
+
var useEffect = stub;
|
|
11
|
+
var useMemo = stub;
|
|
12
|
+
var useReducer = stub;
|
|
13
|
+
var useRef = stub;
|
|
14
|
+
var useState = stub;
|
|
15
|
+
export {
|
|
16
|
+
useCallback,
|
|
17
|
+
useEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
useReducer,
|
|
20
|
+
useRef,
|
|
21
|
+
useState
|
|
22
|
+
};
|
package/dist/services.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Service,
|
|
3
|
+
interpolate
|
|
4
|
+
} from "./chunk-AITGS27R.js";
|
|
5
|
+
import "./chunk-MLKGABMK.js";
|
|
6
|
+
|
|
7
|
+
// src/postgres.ts
|
|
8
|
+
var Postgres = class extends Service {
|
|
9
|
+
image;
|
|
10
|
+
port = 5432;
|
|
11
|
+
user = "realm";
|
|
12
|
+
database = "realm";
|
|
13
|
+
// Underscore-prefixed to keep it off the public surface - the backup
|
|
14
|
+
// strategy is the only consumer, and output doesn't carry it.
|
|
15
|
+
_majorVersion;
|
|
16
|
+
_shmSizeMb;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
super();
|
|
19
|
+
const majorVersion = options?.majorVersion ?? 18;
|
|
20
|
+
this.image = options?.image ?? `postgres:${majorVersion}`;
|
|
21
|
+
this._majorVersion = majorVersion;
|
|
22
|
+
this._shmSizeMb = options?.shmSizeMb ?? 128;
|
|
23
|
+
}
|
|
24
|
+
get password() {
|
|
25
|
+
return this.derived("postgres-password");
|
|
26
|
+
}
|
|
27
|
+
get url() {
|
|
28
|
+
return interpolate`postgres://${this.user}:${this.password}@${this.host}:${this.port}/${this.database}`;
|
|
29
|
+
}
|
|
30
|
+
getServiceDefinition() {
|
|
31
|
+
return {
|
|
32
|
+
image: this.image,
|
|
33
|
+
volumes: [
|
|
34
|
+
{
|
|
35
|
+
hostPath: interpolate`./data/${this.host}`,
|
|
36
|
+
containerPath: "/var/lib/postgresql",
|
|
37
|
+
backup: { kind: "pg-dump", url: this.url, version: this._majorVersion }
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
env: {
|
|
41
|
+
POSTGRES_USER: this.user,
|
|
42
|
+
POSTGRES_PASSWORD: this.password,
|
|
43
|
+
POSTGRES_DB: this.database
|
|
44
|
+
},
|
|
45
|
+
healthcheck: {
|
|
46
|
+
test: ["CMD", "pg_isready", "-U", "realm", "-d", "realm"]
|
|
47
|
+
},
|
|
48
|
+
shmSizeMb: this._shmSizeMb
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/mysql.ts
|
|
54
|
+
var MySQL = class extends Service {
|
|
55
|
+
image;
|
|
56
|
+
port = 3306;
|
|
57
|
+
user = "realm";
|
|
58
|
+
database = "realm";
|
|
59
|
+
_majorVersion;
|
|
60
|
+
constructor(options) {
|
|
61
|
+
super();
|
|
62
|
+
const majorVersion = options?.majorVersion ?? 9;
|
|
63
|
+
this.image = `mysql:${majorVersion}`;
|
|
64
|
+
this._majorVersion = majorVersion;
|
|
65
|
+
}
|
|
66
|
+
get password() {
|
|
67
|
+
return this.derived("mysql-password");
|
|
68
|
+
}
|
|
69
|
+
get rootPassword() {
|
|
70
|
+
return this.derived("root-password");
|
|
71
|
+
}
|
|
72
|
+
get url() {
|
|
73
|
+
return interpolate`mysql://${this.user}:${this.password}@${this.host}:${this.port}/${this.database}`;
|
|
74
|
+
}
|
|
75
|
+
getServiceDefinition() {
|
|
76
|
+
return {
|
|
77
|
+
image: this.image,
|
|
78
|
+
volumes: [
|
|
79
|
+
{
|
|
80
|
+
hostPath: interpolate`./data/${this.host}`,
|
|
81
|
+
containerPath: "/var/lib/mysql",
|
|
82
|
+
backup: { kind: "mysql-dump", url: this.url, version: this._majorVersion }
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
env: {
|
|
86
|
+
MYSQL_USER: this.user,
|
|
87
|
+
MYSQL_PASSWORD: this.password,
|
|
88
|
+
MYSQL_DATABASE: this.database,
|
|
89
|
+
MYSQL_ROOT_PASSWORD: this.rootPassword
|
|
90
|
+
},
|
|
91
|
+
healthcheck: {
|
|
92
|
+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/valkey.ts
|
|
99
|
+
var Valkey = class extends Service {
|
|
100
|
+
image;
|
|
101
|
+
port = 6379;
|
|
102
|
+
constructor(_options) {
|
|
103
|
+
super();
|
|
104
|
+
this.image = "valkey/valkey:8";
|
|
105
|
+
}
|
|
106
|
+
get password() {
|
|
107
|
+
return this.derived("valkey-password");
|
|
108
|
+
}
|
|
109
|
+
get url() {
|
|
110
|
+
return interpolate`redis://default:${this.password}@${this.host}:${this.port}/0`;
|
|
111
|
+
}
|
|
112
|
+
getServiceDefinition() {
|
|
113
|
+
return {
|
|
114
|
+
image: this.image,
|
|
115
|
+
volumes: [
|
|
116
|
+
{
|
|
117
|
+
hostPath: interpolate`./data/${this.host}`,
|
|
118
|
+
containerPath: "/var/lib/valkey/data",
|
|
119
|
+
backup: { kind: "valkey-save", url: this.url }
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
command: ["--requirepass", this.password],
|
|
123
|
+
env: { REDISCLI_AUTH: this.password },
|
|
124
|
+
healthcheck: {
|
|
125
|
+
test: ["CMD", "valkey-cli", "PING"]
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/redis.ts
|
|
132
|
+
var Redis = class extends Service {
|
|
133
|
+
image;
|
|
134
|
+
port = 6379;
|
|
135
|
+
constructor(options) {
|
|
136
|
+
super();
|
|
137
|
+
this.image = `redis:${options?.version ?? "8"}`;
|
|
138
|
+
}
|
|
139
|
+
get password() {
|
|
140
|
+
return this.derived("redis-password");
|
|
141
|
+
}
|
|
142
|
+
get url() {
|
|
143
|
+
return interpolate`redis://default:${this.password}@${this.host}:${this.port}/0`;
|
|
144
|
+
}
|
|
145
|
+
getServiceDefinition() {
|
|
146
|
+
return {
|
|
147
|
+
image: this.image,
|
|
148
|
+
volumes: [
|
|
149
|
+
{
|
|
150
|
+
hostPath: interpolate`./data/${this.host}`,
|
|
151
|
+
containerPath: "/data",
|
|
152
|
+
backup: { kind: "valkey-save", url: this.url }
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
command: ["--requirepass", this.password],
|
|
156
|
+
env: { REDISCLI_AUTH: this.password },
|
|
157
|
+
healthcheck: {
|
|
158
|
+
test: ["CMD", "redis-cli", "PING"]
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/garage.ts
|
|
165
|
+
var Garage = class extends Service {
|
|
166
|
+
image;
|
|
167
|
+
port = 3900;
|
|
168
|
+
adminPort = 3903;
|
|
169
|
+
bucket = "data";
|
|
170
|
+
constructor(options) {
|
|
171
|
+
super();
|
|
172
|
+
this.image = `dxflrs/garage:${options?.version ?? "v1"}`;
|
|
173
|
+
}
|
|
174
|
+
get accessKey() {
|
|
175
|
+
return this.derived("access-key");
|
|
176
|
+
}
|
|
177
|
+
get secretKey() {
|
|
178
|
+
return this.derived("secret-key");
|
|
179
|
+
}
|
|
180
|
+
get url() {
|
|
181
|
+
return interpolate`http://${this.host}:${this.port}`;
|
|
182
|
+
}
|
|
183
|
+
get adminUrl() {
|
|
184
|
+
return interpolate`http://${this.host}:${this.adminPort}`;
|
|
185
|
+
}
|
|
186
|
+
getServiceDefinition() {
|
|
187
|
+
return {
|
|
188
|
+
image: this.image,
|
|
189
|
+
volumes: [
|
|
190
|
+
{
|
|
191
|
+
hostPath: interpolate`./data/${this.host}`,
|
|
192
|
+
containerPath: "/var/lib/garage/data",
|
|
193
|
+
backup: { kind: "file-copy" }
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
env: {
|
|
197
|
+
GARAGE_ACCESS_KEY: this.accessKey,
|
|
198
|
+
GARAGE_SECRET_KEY: this.secretKey,
|
|
199
|
+
GARAGE_DEFAULT_BUCKET: this.bucket
|
|
200
|
+
},
|
|
201
|
+
healthcheck: {
|
|
202
|
+
test: ["CMD", "/garage", "status"]
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
export {
|
|
208
|
+
Garage,
|
|
209
|
+
MySQL,
|
|
210
|
+
Postgres,
|
|
211
|
+
Redis,
|
|
212
|
+
Valkey
|
|
213
|
+
};
|