Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ef4abe5
docs: add micro Supabase stacks design spec
jgoux Jul 7, 2026
cc6a848
docs: Windows supports supabase start via Docker path
jgoux Jul 7, 2026
c15afd4
docs: phase 1 implementation plan (stack changes + fleet daemon)
jgoux Jul 7, 2026
267ca87
feat(fleet): scaffold @supabase/fleet package
jgoux Jul 7, 2026
37537aa
feat(stack): add micro postgres profile and preload-required registry
jgoux Jul 7, 2026
a799f85
feat(stack): PGDATA conf layering (micro.conf + pod.conf includes)
jgoux Jul 7, 2026
ad76b32
fix(stack): harden pgconf include detection, GUC parsing, and pod.con…
jgoux Jul 7, 2026
6f2edb5
feat(stack): provisioned data dirs, micro profile wiring, postgres 17…
jgoux Jul 7, 2026
46f5d43
feat(stack): enableExtension with preload-on-enable restart
jgoux Jul 7, 2026
99816b7
fix(stack): serialize enableExtension to prevent pod.conf write races
jgoux Jul 7, 2026
20d89da
feat(stack): lazy per-service start behind the API proxy
jgoux Jul 7, 2026
7296801
fix(stack): scope waitAllReady() to started services under lazyServices
jgoux Jul 7, 2026
6eb4621
feat(fleet): pod manifest types and template cache keys
jgoux Jul 7, 2026
69ae47e
feat(fleet): copy-on-write directory clone (clonefile/reflink/copy)
jgoux Jul 7, 2026
b22587b
fix(fleet): clean up partial dest before CoW fallback, preserve symlinks
jgoux Jul 7, 2026
ec88d29
feat(fleet): add persistent port registry
jgoux Jul 7, 2026
84e64ff
fix(fleet): harden PortRegistry against corrupt state, add restore()
jgoux Jul 7, 2026
494f0bc
feat(fleet): template store with base and warm templates
jgoux Jul 7, 2026
db0e27d
feat(fleet): pod registry and provisioner (create/reset/fork/destroy)
jgoux Jul 7, 2026
4a0cf4d
fix(fleet): release ports on failed create/fork before manifest write
jgoux Jul 7, 2026
b7fdb5d
feat(fleet): TCP wake-proxy edge with activity events
jgoux Jul 7, 2026
62bcf18
fix(fleet): cap pre-wake buffer and catch throws from wake() resolution
jgoux Jul 7, 2026
9f27406
feat(fleet): idle monitor with connection-aware countdown
jgoux Jul 7, 2026
c22c57c
feat(fleet): fleet facade with wake-on-connect and suspend-on-idle
jgoux Jul 7, 2026
2d5e012
fix(fleet): serialize per-pod lifecycle ops and reap by postmaster.pid
jgoux Jul 7, 2026
51b242b
test(fleet): density e2e and package README
jgoux Jul 7, 2026
f561ded
fix(stack,fleet,cli): address final-review findings for stacks spec
jgoux Jul 7, 2026
cb0078a
Merge branch 'develop' into claude/micro-supabase-stacks-spec
jgoux Jul 7, 2026
5382e0f
fix: regenerate pnpm-lock.yaml to repair broken fleet importer entries
jgoux Jul 7, 2026
fbf039b
Merge remote branch state (develop update) into lockfile fix
jgoux Jul 7, 2026
f1c8854
fix(process-compose): model long-running daemons in mock spawner to f…
jgoux Jul 7, 2026
9fc3f47
chore(cli): address micro stack review feedback
jgoux Jul 7, 2026
69b4bc8
chore(cli): address stack review follow-ups
jgoux Jul 7, 2026
6137e34
chore(cli): address micro stack review follow-ups
jgoux Jul 8, 2026
e31c93f
chore(cli): address additional stack review feedback
jgoux Jul 8, 2026
c17517d
chore(cli): sync lockfile with current base
jgoux Jul 8, 2026
7af4e06
Merge remote-tracking branch 'refs/remotes/origin/develop' into fix/m…
jgoux Jul 8, 2026
6f5953c
test(cli): relax core test timeout for ci
jgoux Jul 8, 2026
f69ab95
fix(fleet): harden micro stack lifecycle
jgoux Jul 8, 2026
327e836
fix(stack): stabilize password rotation
jgoux Jul 8, 2026
04dd218
fix(stack): harden lazy services and fleet ports
jgoux Jul 8, 2026
6162cac
fix(stack): enforce native mode, idle-suspend re-check, and typed dae…
jgoux Jul 9, 2026
64a0b39
fix(fleet): preserve warm template on reset and keep password out of …
jgoux Jul 9, 2026
d622d41
Merge branch 'develop' into claude/micro-supabase-stacks-spec
jgoux Jul 9, 2026
8fd60f8
chore(deps): resolve lockfile drift for fleet importer after develop …
jgoux Jul 9, 2026
38a078e
fix(stack): harden password handling and micro-profile edge cases
jgoux Jul 9, 2026
ad69f35
fix(fleet): harden manifest validation, startup reaping, and pod.conf…
jgoux Jul 9, 2026
db9ea2f
fix(fleet): guard corrupt pod dirs on create and fix lazy restart tra…
jgoux Jul 9, 2026
99e1e8b
fix(stack): keep docker env values out of argv and harden manifest pa…
jgoux Jul 9, 2026
0d7d037
fix(stack): close remaining docker argv leaks and micro/lazy edge cases
jgoux Jul 9, 2026
5d77e18
fix(stack): fail lazy waitAllReady until start() completes
jgoux Jul 9, 2026
11e7249
fix(stack): route remote waitReady through the daemon and re-run init…
jgoux Jul 9, 2026
1d15a41
fix(fleet): single-daemon root lock and stricter startup/creation val…
jgoux Jul 9, 2026
d827faa
refactor(stack): centralize provisioned fleet runtime
jgoux Jul 17, 2026
53a6dcb
refactor(stack): own lazy service activation
jgoux Jul 17, 2026
f3613e1
refactor(stack): expose fleet as stack subpath
jgoux Jul 17, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
resolveProjectSubtree,
type ProjectConfig,
} from "@supabase/config";
import type { EdgeRuntimeConfig } from "@supabase/stack/effect";
import type { EdgeRuntimeReloadOptions } from "@supabase/stack/effect";
import { Data, Effect, Redacted } from "effect";
import { ProjectHome } from "../../../config/project-home.service.ts";

Expand All @@ -30,7 +30,7 @@ export class FunctionsDevEdgeRuntimeDisabledError extends Data.TaggedError(
}

export interface ResolvedFunctionsDevEdgeRuntimeConfig {
readonly config: EdgeRuntimeConfig;
readonly config: EdgeRuntimeReloadOptions;
readonly fingerprint: string;
}

Expand Down Expand Up @@ -64,7 +64,7 @@ function stableRecord(
);
}

function fingerprintEdgeRuntimeConfig(config: EdgeRuntimeConfig): string {
function fingerprintEdgeRuntimeConfig(config: EdgeRuntimeReloadOptions): string {
return JSON.stringify({
enabled: config.enabled,
inspectorPort: config.inspectorPort,
Expand All @@ -73,7 +73,9 @@ function fingerprintEdgeRuntimeConfig(config: EdgeRuntimeConfig): string {
});
}

function toStackEdgeRuntimeConfig(config: ResolvedProjectEdgeRuntimeConfig): EdgeRuntimeConfig {
function toStackEdgeRuntimeConfig(
config: ResolvedProjectEdgeRuntimeConfig,
): EdgeRuntimeReloadOptions {
return {
enabled: config.enabled,
inspectorPort: config.inspector_port,
Expand Down
48 changes: 24 additions & 24 deletions apps/cli/src/next/config/stack-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { StackConfig, VersionManifest } from "@supabase/stack/effect";
import { STACK_SERVICE_NAMES } from "@supabase/stack/effect";
import type { StackConfig, StackServiceName, VersionManifest } from "@supabase/stack/effect";

export const excludedStackServices = [
"auth",
Expand All @@ -22,23 +23,22 @@ export function toStartStackConfig(
exclude: ReadonlyArray<ExcludedStackService>,
mode: StartMode,
): StackConfig {
const excluded = new Set(exclude);
const excluded = new Set<string>(exclude);
if (excluded.has("storage")) excluded.add("imgproxy");
if (excluded.has("pgmeta")) excluded.add("studio");
if (excluded.has("analytics")) excluded.add("vector");
const services = STACK_SERVICE_NAMES.filter(
(service) => service !== "edge-runtime" || mode !== "native",
).filter((service) => !excluded.has(service));
return {
mode,
realtime: excluded.has("realtime") ? false : {},
storage: excluded.has("storage") ? false : {},
imgproxy: excluded.has("imgproxy") || excluded.has("storage") ? false : {},
mailpit: excluded.has("mailpit") ? false : {},
pgmeta: excluded.has("pgmeta") ? false : {},
studio: excluded.has("studio") || excluded.has("pgmeta") ? false : {},
analytics: excluded.has("analytics") ? false : {},
vector: excluded.has("vector") || excluded.has("analytics") ? false : {},
pooler: excluded.has("pooler") ? false : {},
...(excluded.has("auth") ? { auth: false } : {}),
...(excluded.has("postgrest") ? { postgrest: false } : {}),
services,
};
}

const hasService = (stackConfig: StackConfig, service: StackServiceName): boolean =>
stackConfig.services?.includes(service) ?? false;

export function withServiceVersions(
stackConfig: StackConfig,
versions: Partial<VersionManifest>,
Expand All @@ -50,47 +50,47 @@ export function withServiceVersions(
? stackConfig.postgres
: { ...stackConfig.postgres, version: versions.postgres },
postgrest:
stackConfig.postgrest === false || versions.postgrest === undefined
!hasService(stackConfig, "postgrest") || versions.postgrest === undefined
? stackConfig.postgrest
: { ...stackConfig.postgrest, version: versions.postgrest },
auth:
stackConfig.auth === false || versions.auth === undefined
!hasService(stackConfig, "auth") || versions.auth === undefined
? stackConfig.auth
: { ...stackConfig.auth, version: versions.auth },
realtime:
stackConfig.realtime === false || versions.realtime === undefined
!hasService(stackConfig, "realtime") || versions.realtime === undefined
? stackConfig.realtime
: { ...stackConfig.realtime, version: versions.realtime },
storage:
stackConfig.storage === false || versions.storage === undefined
!hasService(stackConfig, "storage") || versions.storage === undefined
? stackConfig.storage
: { ...stackConfig.storage, version: versions.storage },
imgproxy:
stackConfig.imgproxy === false || versions.imgproxy === undefined
!hasService(stackConfig, "imgproxy") || versions.imgproxy === undefined
? stackConfig.imgproxy
: { ...stackConfig.imgproxy, version: versions.imgproxy },
mailpit:
stackConfig.mailpit === false || versions.mailpit === undefined
!hasService(stackConfig, "mailpit") || versions.mailpit === undefined
? stackConfig.mailpit
: { ...stackConfig.mailpit, version: versions.mailpit },
pgmeta:
stackConfig.pgmeta === false || versions.pgmeta === undefined
!hasService(stackConfig, "pgmeta") || versions.pgmeta === undefined
? stackConfig.pgmeta
: { ...stackConfig.pgmeta, version: versions.pgmeta },
studio:
stackConfig.studio === false || versions.studio === undefined
!hasService(stackConfig, "studio") || versions.studio === undefined
? stackConfig.studio
: { ...stackConfig.studio, version: versions.studio },
analytics:
stackConfig.analytics === false || versions.analytics === undefined
!hasService(stackConfig, "analytics") || versions.analytics === undefined
? stackConfig.analytics
: { ...stackConfig.analytics, version: versions.analytics },
vector:
stackConfig.vector === false || versions.vector === undefined
!hasService(stackConfig, "vector") || versions.vector === undefined
? stackConfig.vector
: { ...stackConfig.vector, version: versions.vector },
pooler:
stackConfig.pooler === false || versions.pooler === undefined
!hasService(stackConfig, "pooler") || versions.pooler === undefined
? stackConfig.pooler
: { ...stackConfig.pooler, version: versions.pooler },
};
Expand Down
30 changes: 12 additions & 18 deletions apps/cli/src/next/config/stack-config.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,10 @@ describe("toStartStackConfig", () => {
});

it("dedupes excluded services when building stack config", () => {
expect(toStartStackConfig(["auth", "auth"], "auto")).toMatchObject({
mode: "auto",
auth: false,
});
expect(toStartStackConfig(["auth", "postgrest"], "auto")).toMatchObject({
mode: "auto",
auth: false,
postgrest: false,
});
expect(toStartStackConfig(["auth", "auth"], "auto").services).not.toContain("auth");
expect(toStartStackConfig(["auth", "postgrest"], "auto").services).toEqual(
expect.not.arrayContaining(["auth", "postgrest"]),
);
});
});

Expand All @@ -39,16 +34,15 @@ describe("withServiceVersions", () => {
realtime: { version: "2.78.10" },
});

expect(
withServiceVersions(toStartStackConfig(["auth", "storage"], "auto"), {
postgres: "17.6.1.090",
auth: "2.187.0",
storage: "1.39.2",
}),
).toMatchObject({
const excluded = withServiceVersions(toStartStackConfig(["auth", "storage"], "auto"), {
postgres: "17.6.1.090",
auth: "2.187.0",
storage: "1.39.2",
});
expect(excluded).toMatchObject({
postgres: { version: "17.6.1.090" },
auth: false,
storage: false,
});
expect(excluded.auth).toBeUndefined();
expect(excluded.storage).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions apps/cli/tests/helpers/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ export function mockStack(
startService: () => Effect.void,
stopService: () => Effect.void,
restartService: () => Effect.void,
ensureExtensionPreload: () => Effect.void,
reloadFunctions: () => Effect.void,
reloadEdgeRuntime: () => Effect.void,
getState: () =>
Expand Down
4 changes: 4 additions & 0 deletions apps/cli/tests/helpers/running-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ function makeStackLayer(opts: {
opts.states.some((state) => state.name === name)
? Effect.void
: Effect.fail(new ServiceNotFoundError({ name })),
ensureExtensionPreload: (name: string) =>
opts.states.some((state) => state.name === name)
? Effect.void
: Effect.fail(new ServiceNotFoundError({ name })),
reloadFunctions: () =>
opts.states.some((state) => state.name === "edge-runtime")
? Effect.void
Expand Down
2 changes: 2 additions & 0 deletions apps/cli/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ export default defineConfig({
test: {
name: "unit",
include: ["**/*.unit.test.ts"],
testTimeout: 30_000,
},
},
{
plugins: [dockerfileTextPlugin()],
test: {
name: "integration",
include: ["**/*.integration.test.ts"],
testTimeout: 30_000,
},
},
{
Expand Down
Loading