+ No install required. The demo runs entirely in your browser
+ against fixture data.
+
);
diff --git a/apps/web/src/components/demo/demo-banner.tsx b/apps/web/src/components/demo/demo-banner.tsx
index 54bd7ce4..65790534 100644
--- a/apps/web/src/components/demo/demo-banner.tsx
+++ b/apps/web/src/components/demo/demo-banner.tsx
@@ -1,15 +1,19 @@
"use client";
import { Button } from "@ui/button";
-import { ArrowRight, Sparkles, X } from "lucide-react";
-import { useState } from "react";
+import { ArrowRight, LogOut, Sparkles } from "lucide-react";
import { useWaitlist } from "@/components/landing/waitlist-dialog";
+import { useAgentEndpoint } from "@/lib/agent-endpoint";
+/**
+ * Persistent banner shown above the studio header whenever the active agent
+ * endpoint is the in-browser MSW mock. Communicates the limitations of demo
+ * mode (no real execution, fixture data) and offers two escape hatches:
+ * sign up for the beta, or switch back to the local agent.
+ */
export function DemoBanner() {
- const [dismissed, setDismissed] = useState(false);
const waitlist = useWaitlist();
-
- if (dismissed) return null;
+ const { useLocal } = useAgentEndpoint();
return (
@@ -18,8 +22,8 @@ export function DemoBanner() {
Demo mode.{" "}
- Realistic fixture data. Actions are no-ops. Pair a real local
- agent to use the actual Studio.
+ You're running the real Studio against fixture data. Writes are
+ no-ops; install the CLI to use a live local agent.
@@ -35,11 +39,12 @@ export function DemoBanner() {
- You change .stack/config.nix — the
- Stackpanel agent regenerates IDE settings, the Caddyfile, your{" "}
- @gen/env packages, deploy
- manifests, and more. Always atomic, always in sync with your team.
-
-
-
-
-
-
-
-
-
Path
-
- Generated by
-
-
Size
-
- Updated
-
-
-
-
- {DEMO_GENERATED_FILES.map((file) => (
-
-
-
-
-
- {file.path}
-
-
-
-
-
- {file.tool}
-
-
-
- {formatBytes(file.bytes)}
-
-
-
-
- {file.updated}
-
-
-
- ))}
-
-
-
-
-
-
-
-
- Why generated files (and not symlinks)?
-
-
- Stackpanel writes real files into your repo so they show up in PRs,
- work in CI, and stay readable when your editor isn't inside
- the devshell. The local Go agent debounces writes and rolls back
- on errors, so you never end up with a half-written manifest.
-
- Caddy reverse-proxies *.acme.local{" "}
- to your dev ports. Step CA issues per-device certs your OS already
- trusts. No more localhost:5173{" "}
- gymnastics.
-
- Stackpanel runs a project-scoped Step CA and installs the root
- into your OS trust store on first devshell entry. WebAuthn,
- secure cookies and HTTPS-only APIs all just work — locally.
-
- No Docker daemon, no runtime drift. The same packages your team
- gets via{" "}
- stack.globalServices.postgres{" "}
- or stack.globalServices.redis{" "}
- land on every machine, every time.
-
- Edit per environment. Stackpanel re-keys files for every team
- member, syncs them to Cloudflare / Fly / Workers, and ships a
- type-safe @gen/env package to
- each app.
-
- );
-}
-
-function ValueCell({
- value,
- reveal,
- encrypted,
-}: {
- value: string;
- reveal: boolean;
- encrypted?: boolean;
-}) {
- if (encrypted && !reveal) {
- return (
- ••••••••
- );
- }
- return {value};
-}
diff --git a/apps/web/src/routes/studio.tsx b/apps/web/src/routes/studio.tsx
index 9fd9fb3d..95e52264 100644
--- a/apps/web/src/routes/studio.tsx
+++ b/apps/web/src/routes/studio.tsx
@@ -1,12 +1,14 @@
import { createFileRoute, Outlet, useLocation } from "@tanstack/react-router";
-import { useMemo } from "react";
+import { useEffect, useMemo } from "react";
import { AgentConnect } from "@/components/agent-connect";
+import { DemoBanner } from "@/components/demo/demo-banner";
import { DashboardHeader } from "@/components/studio/dashboard-header";
import { DashboardSidebar } from "@/components/studio/dashboard-sidebar";
import {
- SidebarInset,
- SidebarProvider,
+ SidebarInset,
+ SidebarProvider,
} from "@/components/ui/sidebar";
+import { useAgentEndpoint } from "@/lib/agent-endpoint";
import { AgentProvider, useAgentContext } from "@/lib/agent-provider";
import { AgentSSEProvider } from "@/lib/agent-sse-provider";
import { useAgentLiveQuerySync } from "@/lib/use-agent";
@@ -14,104 +16,137 @@ import { ProjectProvider } from "@/lib/project-provider";
import { cn } from "@/lib/utils";
import { FeatureFlagProvider } from "@gen/featureflags";
-// Search params for optional project selection
+// Search params for optional project selection or auto-demo entry.
+//
+// `demo` is intentionally typed as `string` (rather than `boolean`) so the
+// search-params record stays a `Record` — that
+// shape is what `new URLSearchParams(search)` callers elsewhere in the
+// sidebar/panels code rely on. The truthiness check inside `StudioLayout`
+// treats any present value as "enable demo", so `?demo=1` (the marketing
+// link) and `?demo=true` both work.
interface StudioSearchParams {
- project?: string;
-}
-
-function getStudioAgentConfig() {
- const host = import.meta.env.VITE_STACKPANEL_AGENT_HOST || "localhost";
- const parsedPort = Number.parseInt(
- import.meta.env.VITE_STACKPANEL_AGENT_PORT || "",
- 10,
- );
- const token = import.meta.env.VITE_STACKPANEL_AGENT_TOKEN || undefined;
-
- return {
- host,
- port: Number.isFinite(parsedPort) ? parsedPort : 9876,
- token,
- };
+ project?: string;
+ demo?: string;
}
export const Route = createFileRoute("/studio")({
- component: StudioLayout,
- validateSearch: (search: Record): StudioSearchParams => {
- return {
- project: typeof search.project === "string" ? search.project : undefined,
- };
- },
+ component: StudioLayout,
+ validateSearch: (search: Record): StudioSearchParams => {
+ return {
+ project: typeof search.project === "string" ? search.project : undefined,
+ demo:
+ search.demo === "1" || search.demo === true || search.demo === "true"
+ ? "1"
+ : undefined,
+ };
+ },
});
function StudioLayout() {
- const { project } = Route.useSearch();
- const { host, port, token } = getStudioAgentConfig();
+ const { project, demo } = Route.useSearch();
+ const { endpoint, isDemo, bootingDemo, useDemo } = useAgentEndpoint();
+
+ // `?demo=1` is the marketing entry-point: flip into demo mode on mount.
+ useEffect(() => {
+ if (demo && !isDemo) {
+ void useDemo();
+ }
+ }, [demo, isDemo, useDemo]);
+
+ const { host, port, token } = endpoint;
+ // Force a clean remount of SSE/Agent providers whenever the endpoint
+ // changes so internal connection state (EventSource, polling timers,
+ // cached health) doesn't leak across local <-> demo transitions.
+ const providerKey = `${endpoint.kind}:${host}:${port}`;
+
+ if (bootingDemo) {
+ return (
+
+ Booting demo agent…
+
+ );
+ }
- return (
- // SSE provider is outside so AgentProvider can consume SSE status for health
-
-
-
-
-
-
-
- {/* */}
-
-
-
-
-
-
-
-
-
- );
+ return (
+ // SSE provider is outside so AgentProvider can consume SSE status for health
+
+
+
+
+
+
+
+ {/* */}
+
+ {isDemo && }
+
+
+
+
+
+
+
+
+ );
}
function AgentQuerySync() {
- useAgentLiveQuerySync();
- return null;
+ useAgentLiveQuerySync();
+ return null;
}
-function EnsureAgent() {
- const { isConnected } = useAgentContext();
- const location = useLocation();
- const isOverview = location.pathname === "/studio";
- const isSetup = location.pathname === "/studio/setup";
+function EnsureAgent({ isDemo }: { isDemo: boolean }) {
+ const { isConnected } = useAgentContext();
+ const location = useLocation();
+ const isOverview = location.pathname === "/studio";
+ const isSetup = location.pathname === "/studio/setup";
- const isAgentVisible = useMemo(() => {
- if (isSetup) return !isConnected;
- return !isConnected;
- }, [isSetup, isConnected]);
+ // In demo mode the (mocked) agent is always "connected" — never show the
+ // pairing UI or the disabled overlay.
+ const isAgentVisible = useMemo(() => {
+ if (isDemo) return false;
+ if (isSetup) return !isConnected;
+ return !isConnected;
+ }, [isDemo, isSetup, isConnected]);
- const isOverlayVisible = useMemo(() => {
- if (isOverview) return false;
- if (isSetup) return false;
- return !isConnected;
- }, [isSetup, isConnected, isOverview]);
+ const isOverlayVisible = useMemo(() => {
+ if (isDemo) return false;
+ if (isOverview) return false;
+ if (isSetup) return false;
+ return !isConnected;
+ }, [isDemo, isSetup, isConnected, isOverview]);
- return (
-
-
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+
+
+
+ );
}
diff --git a/bun.lock b/bun.lock
index fcb2e9d2..2c74f62e 100644
--- a/bun.lock
+++ b/bun.lock
@@ -252,6 +252,7 @@
"babel-plugin-react-compiler": "^1.0.0",
"happy-dom": "^20.1.0",
"jsdom": "^26.0.0",
+ "msw": "^2.7.0",
"postcss": "^8.5.6",
"typescript": "^5",
"vite": "^8.0.7",
@@ -4828,7 +4829,7 @@
"type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="],
- "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
+ "type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
@@ -5654,8 +5655,6 @@
"msw/tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="],
- "msw/type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
-
"msw/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"mysql/readable-stream": ["readable-stream@2.3.7", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw=="],
@@ -5828,6 +5827,8 @@
"tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+ "ts-jest/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
+
"ts-node/diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="],
"tsx/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
diff --git a/issues.jsonl b/issues.jsonl
new file mode 100644
index 00000000..e3eeda0c
--- /dev/null
+++ b/issues.jsonl
@@ -0,0 +1,56 @@
+{"id":"stackpanel-r7g","title":"Fix broken bun install on main: alchemy-effect catalog reference","description":"Five packages reference `alchemy-effect: catalog:` (apps/api, apps/docs, apps/web, packages/db, packages/infra) but the root package.json#workspaces.catalog has no alchemy-effect entry. Result: bun install --frozen-lockfile fails with 'alchemy-effect@catalog: failed to resolve' on a clean clone of main.\n\nReproduction:\n rm -rf node_modules\n bun install --frozen-lockfile\n # error: alchemy-effect@catalog: failed to resolve (x5)\n\nRoot cause: introduced in commit dda9c459 'refactor: replace AWS EC2 infra with Cloudflare Workers + Neon, add agenix module' — the dep was added to packages but the catalog entry was never added.\n\nFix: add \"alchemy-effect\": \"^0.12.0\" to root package.json#workspaces.catalog (bun.lock already resolves alchemy-effect@0.12.0).\n\nSurfaced while working on PR #15 — could not run vite to regen routeTree.gen.ts after route deletions, had to hand-edit. Worktree node_modules from before the regression still work, masking the issue locally.","status":"closed","priority":1,"issue_type":"bug","owner":"me@cooperm.com","created_at":"2026-04-29T08:31:32Z","created_by":"Cooper Maruyama","updated_at":"2026-04-29T09:16:42Z","closed_at":"2026-04-29T09:16:42Z","close_reason":"Fixed by PR #16 — migrated workspace to alchemy@2.0.0-beta.20 (alchemy-effect rebrand). bun install now resolves cleanly and lockfile regenerates without the catalog miss.","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-os2.8","title":"Add Hetzner provision regression test with ephemeral instances","description":"Add a reproducible regression test for stackpanel provision using ephemeral Hetzner Cloud instances created on-demand via the hcloud API. The token already exists in SOPS as hetzner_api_key. The implementation should add the hcloud CLI to the devshell, create a disposable-machine test script, load hetzner_api_key from SOPS into HCLOUD_TOKEN, inject a temporary machine via .stack/config.local.nix, run stackpanel provision against it, verify the resulting NixOS host, and always clean up the instance.","design":"Prefer a real end-to-end infrastructure regression test over mocks for the final provision path, but keep verification safe and deterministic where possible. Use existing shell smoke test patterns for script structure and use .stack/config.local.nix for the highest-priority temporary machine override.","acceptance_criteria":"- hcloud is available in the devshell\n- tests/provision-hetzner-e2e.sh provisions an ephemeral CX22 in fsn1 from Debian 12\n- The script exports HCLOUD_TOKEN from the SOPS key hetzner_api_key\n- The script injects machine config via .stack/config.local.nix and cleans up in a trap\n- Justfile exposes a command to run the regression test and a dry-run mode","status":"closed","priority":1,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-03-29T08:03:28Z","created_by":"Cooper Maruyama","updated_at":"2026-03-29T08:13:29Z","closed_at":"2026-03-29T08:13:29Z","close_reason":"Implemented: added hcloud to devshell, tests/provision-hetzner-e2e.sh, and Justfile entries. Commit d54bdbc7.","labels":["deployment","hetzner","testing"],"dependencies":[{"issue_id":"stackpanel-os2.8","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-29T01:03:27Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-foe.4","title":"P4: Green test matrix on stackpanel","description":"Real deploys (not dry-run) of {docs,web} x {colmena,nixos-rebuild,fly} on stackpanel infra. Ensure docs and web have Nix packages that build to deployable artifacts. Deploy to ovh-usw-1 (direct) for NixOS backends. Add nix flake check validation for deployment outputs. All 6 cells must go green.","status":"open","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:32Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:39:32Z","dependencies":[{"issue_id":"stackpanel-foe.4","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:32Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.4","depends_on_id":"stackpanel-foe.2","type":"blocks","created_at":"2026-03-28T13:47:41Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.4","depends_on_id":"stackpanel-foe.3","type":"blocks","created_at":"2026-03-28T13:47:42Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-foe.3","title":"P3: Implement Fly as first plugin backend","description":"Create nix/stackpanel/modules/fly/ with meta.nix (features.deployBackend=true) and module.nix. Register deployment.backends.fly. Inject app.fly.* options via appModules (appName, region, vm config). Generate fly.toml using pkgs.lib.generators.toTOML. Implement scripts.deploy (generate fly.toml, call fly deploy) and scripts.dryRun. Add fly config to stackpanel apps for testing. This is the litmus test for the plugin contract.","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:30Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:19Z","closed_at":"2026-04-20T22:37:19Z","close_reason":"Fly backend implemented at nix/stackpanel/deployment/fly/module.nix with Go CLI support in deploy.go.","dependencies":[{"issue_id":"stackpanel-foe.3","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:30Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.3","depends_on_id":"stackpanel-foe.1","type":"blocks","created_at":"2026-03-28T13:47:40Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-foe.2","title":"P2: Port existing backends to script modules","description":"Move colmena, nixos-rebuild, and alchemy from Go functions to Nix script modules. Identical commands, different packaging. Colmena: extract deployColmena() into modules/deploy/backends/colmena.nix. Nixos-rebuild: extract deployNixosRebuild() into backends/nixos-rebuild.nix. Alchemy: extract deployAlchemy() into backends/alchemy.nix. Verify identical behavior. Delete Go switch/case and backend-specific functions. NixOS business logic is FROZEN — same commands, same args.","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:29Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:12Z","closed_at":"2026-04-20T22:37:12Z","close_reason":"All existing backends (colmena, nixos-rebuild, alchemy, fly, cloudflare, aws) ported to modules under nix/stackpanel/deployment/.","dependencies":[{"issue_id":"stackpanel-foe.2","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:28Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.2","depends_on_id":"stackpanel-foe.1","type":"blocks","created_at":"2026-03-28T13:47:39Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-foe.1","title":"P1: Define backend contract \u0026 scaffold generic executor","description":"Define deployment.backends option type (registry with scripts.deploy/dryRun/status/validate). Define Nix args shape (pkgs, lib, app, backendConfig, machines, projectRoot, stateDir). Add deployment.specs computed option. Define context.json schema (dryRun, gitRevision, timestamp, env, targets, secretRefs). Refactor Go CLI deploy.go to generic executor. Update nix eval expression to return resolved specs.","status":"closed","priority":1,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:27Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:05Z","closed_at":"2026-04-20T22:37:05Z","close_reason":"Backend contract defined in nix/stackpanel/modules/deploy/module.nix with deployment.backend enum. Generic executor implemented in apps/stackpanel-go/cmd/cli/deploy.go.","dependencies":[{"issue_id":"stackpanel-foe.1","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:27Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-foe","title":"Pluggable deploy backends","description":"Restructure deployment around pluggable backend modules. Backends become stackpanel modules that provide scripts (Nix-evaluated bash), the Go CLI becomes a generic executor, and Fly is the first plugin to validate the design. Test matrix: {docs,web} x {colmena,nixos-rebuild,fly} on stackpanel + nixmac dogfood. See openspec/changes/pluggable-deploy-backends/ for full design.","status":"closed","priority":1,"issue_type":"epic","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:18Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T23:28:15Z","closed_at":"2026-04-20T23:28:15Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-rvr.2","title":"Switch secrets agent writes to YAML instead of full config rewrites","description":"Replace the current secrets write paths that call WriteConsolidatedData or writeNixEntityJSON(\"secrets\", ...) with YAML-oriented agent APIs.\n\nAcceptance criteria:\n- Secrets-related agent write paths no longer use ReadConsolidatedData plus WriteConsolidatedData for Studio-managed secrets settings\n- Recipient and secrets-config mutations write YAML directly, using sops set where appropriate\n- The old full-entity SetSecrets path is no longer used for Studio-managed secrets flows\n- Server-side tests cover the new YAML mutation behavior and protect against regressions back to config rewrites","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T16:34:01Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:33Z","closed_at":"2026-04-20T22:37:33Z","close_reason":"All agent write endpoints (handleSecretsSet, handleSecretsWrite, writeGroupSecrets) now write to SOPS YAML. updateSecretsNix() is deprecated no-op.","dependencies":[{"issue_id":"stackpanel-rvr.2","depends_on_id":"stackpanel-rvr","type":"parent-child","created_at":"2026-03-28T09:34:00Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-rvr.2","depends_on_id":"stackpanel-rvr.1","type":"blocks","created_at":"2026-03-28T09:34:05Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-rvr.1","title":"Add YAML-backed storage for studio-managed secrets metadata","description":"Create the YAML storage layout for the secrets settings that Studio owns today, instead of persisting those settings under secrets in .stack/config.nix.\n\nAcceptance criteria:\n- Define the YAML files that will hold Studio-managed secrets metadata, including recipient groups, creation rules, and any KMS-related fields that should no longer live in .stack/config.nix\n- Nix/runtime loading reads the YAML-backed data without requiring eval-\u003eJSON-\u003eserialize roundtrips of .stack/config.nix\n- The design explicitly uses sops set for YAML mutations where encrypted YAML needs to be updated\n- Existing hand-written Nix in .stack/config.nix is no longer the source of truth for Studio-managed secrets settings","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T16:33:48Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:26Z","closed_at":"2026-04-20T22:37:26Z","close_reason":"YAML-backed storage implemented in .stack/secrets/vars/*.sops.yaml with Go code in secrets_groups.go and sops.go.","dependencies":[{"issue_id":"stackpanel-rvr.1","depends_on_id":"stackpanel-rvr","type":"parent-child","created_at":"2026-03-28T09:33:47Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-rvr","title":"Move studio secrets config out of config.nix","description":"Studio-managed secrets settings should stop mutating .stack/config.nix through eval-and-reserialize paths.\n\nUse YAML-backed secrets metadata and sops set for YAML mutation so the Studio can update secret values and related secrets config without evaluating away user-authored Nix. Keep tree-sitter/AST patching as the only remaining strategy for code paths that truly still need to edit Nix source.","status":"closed","priority":1,"issue_type":"epic","owner":"me@cooperm.com","created_at":"2026-03-28T16:33:14Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:47Z","closed_at":"2026-04-20T22:37:47Z","close_reason":"all steps complete","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-os2.4","title":"Complete backend adapters for Alchemy secrets and Fly deployments","description":"deploy.go resolves alchemy and fly backends, but only a minimal Alchemy path is wired today and Fly is not implemented in the command dispatcher. Finish the hosted-backend adapters so the CLI covers the backends already modeled in config/docs, including safe secret handling and builder preflight where needed.","design":"Preserve the current backend resolution rules (deployment.backend first, legacy deployment.host fallback) while moving secret handling into temp files or another non-argv mechanism.","acceptance_criteria":"- Alchemy deploy uses env-specific secret material without passing secrets on argv\n- Fly deployments build/publish the expected container artifact and invoke Fly with clear preflight failures when Linux builders are unavailable\n- Backend command construction is covered by tests or mocks\n- Hosted-backend failures surface actionable, backend-specific error messages","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:36Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:29Z","closed_at":"2026-03-28T20:19:29Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-376","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.4","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:35Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.4","depends_on_id":"stackpanel-os2.2","type":"blocks","created_at":"2026-03-28T08:02:39Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-os2.3","title":"Add machine-target deploy flow to stackpanel deploy","description":"docs/design/deploy-command.md specifies stackpanel deploy --target \u003cmachine\u003e so operators can redeploy all apps assigned to a host, but the current command only accepts an app name. Implement the machine-targeted flow and align the no-arg listing/status output with that machine-aware model.","design":"Reuse one config evaluation per command and derive the app set from deployment.targets rather than shelling out once per app.","acceptance_criteria":"- stackpanel deploy --target \u003cmachine\u003e deploys every app mapped to that machine via deployment.targets\n- Missing or empty machine targets fail with explicit errors\n- No-arg deployment listing surfaces both machines and deployable apps consistently\n- Add tests covering machine-to-app resolution and execution ordering","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:35Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:29Z","closed_at":"2026-03-28T20:19:29Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-377","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.3","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:35Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.3","depends_on_id":"stackpanel-os2.2","type":"blocks","created_at":"2026-03-28T08:02:39Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-os2.1","title":"Add Nix deployment output validation to flake checks","description":"The repo already exposes deployment-oriented flake outputs through nix/stackpanel/lib/deploy.nix and nix/flake/global-outputs.nix, but I could not find matching check coverage that forces broken nixosConfigurations / colmena wiring to fail before someone runs a real deploy. Add validation so deploy regressions are caught during normal flake checks and CI.","design":"Keep the deploy output path pure and reviewable; add checks in the flake/check layer rather than baking deploy-time behavior into runtime commands.","acceptance_criteria":"- nix flake check --impure validates the generated nixosConfigurations for configured machines (or an explicitly documented equivalent check)\n- Broken deploy module wiring fails before runtime deployment commands are attempted\n- Coverage includes unprovisioned-machine stubs vs provisioned hardware/disk layouts where relevant\n- Any added checks are documented near the flake output wiring","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:34Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:29Z","closed_at":"2026-03-28T20:19:29Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-378","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.1","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:33Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-os2.2","title":"Finish structured deploy CLI flags and status output","description":"apps/stackpanel-go/cmd/cli/deploy.go currently supports basic app deploys with --dry-run plus human-readable status, but the design doc calls for env overrides, machine-readable output, and richer status metadata. Finish the command surface so deployment can run cleanly in CI and other tooling without scraping terminal text.","design":"Centralize deploy result formatting in deploy.go and keep non-interactive output first-class from the start.","acceptance_criteria":"- stackpanel deploy \u003capp\u003e supports --env to override deployment.defaultEnv\n- stackpanel deploy status [app] supports --json with stable machine-readable output\n- Success and failure paths include backend/target/env information without relying on TUI-only formatting\n- Add Go tests for flag resolution and JSON/status serialization","status":"closed","priority":1,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:34Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:29Z","closed_at":"2026-03-28T20:19:29Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-379","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.2","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:34Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.2","depends_on_id":"stackpanel-os2.1","type":"blocks","created_at":"2026-03-28T08:02:38Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-os2","title":"Complete the deployment feature","description":"Deployment is partially implemented across Nix outputs, Go CLI commands, host-specific backends, docs, and the Studio deploy panel. The current repo has the core Nix scaffolding in nix/stackpanel/modules/deploy/module.nix and nix/stackpanel/lib/deploy.nix, app deploy/provision commands in apps/stackpanel-go/cmd/cli/{deploy,provision}.go, and a Colmena-centric UI in apps/web/src/components/studio/panels/deploy/deploy-panel.tsx. This epic tracks the remaining work needed to make deployment feel complete and coherent against docs/design/deploy-command.md and docs/design/provisioning.md.","design":"Use docs/design/deploy-command.md and docs/design/provisioning.md as the design source of truth, but scope child tasks to repo realities already present in code.","acceptance_criteria":"- Break remaining deployment work into executable child issues\n- Finish CLI, backend, provisioning, UI, and docs gaps\n- Land a validated end-to-end deployment story for NixOS machines and supported hosted backends","status":"closed","priority":1,"issue_type":"epic","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:33Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:30Z","closed_at":"2026-03-28T20:19:30Z","close_reason":"all steps complete","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-380","labels":["deployment"],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-382","title":"Generate MSW demo fixtures from proto-nix example fields","description":"Replace hand-written MSW handlers in apps/web/src/demo/handlers.ts with fixtures generated from the proto-nix schema 'example' fields.\n\nWhy: PR #15 (demo via endpoint swap) ships the MSW agent with hand-curated fixtures. These will drift from the real entity shapes as schemas evolve. The proto-nix system already supports an 'example' field on every scalar (see nix/stackpanel/db/lib/field.nix proto.withExample), but only nix/stackpanel/db/schemas/theme.proto.nix populates it.\n\nScope:\n1. Migrate apps, services, variables, secrets schemas to mkSpField with populated example values\n2. Add mkExampleData to nix/stackpanel/db/lib/ that walks a proto message and emits a JSON object using each field's example (or a deterministic placeholder if missing)\n3. Add a Nix output (flake.packages.${system}.demoFixtures) that generates apps/web/src/demo/fixtures.generated.json\n4. Wire MSW handlers in apps/web/src/demo/handlers.ts to consume the generated JSON; delete the hand-written fixtures we replace\n\nAcceptance: demo fixture data is auto-derived from the same source-of-truth schemas the agent and TS client already use; adding a new field to a proto-nix schema makes it appear in the demo without touching apps/web.\n\nDiscovered while implementing PR #15 https://github.com/darkmatter/stackpanel/pull/15","status":"open","priority":2,"issue_type":"feature","owner":"me@cooperm.com","created_at":"2026-04-29T08:31:31Z","created_by":"Cooper Maruyama","updated_at":"2026-04-29T08:31:31Z","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-dh5","title":"docs.stackpanel.com production deploy throws CF 1101 at runtime","description":"Background: cloud-gate-foundation upgraded alchemy-effect 0.9.0 → 0.12.0. apps/web deploys cleanly to local.stackpanel.com. apps/docs production deploy succeeds at the CF API level but every request returns CF error 1101 (Worker exception).\n\nWhat was tried (all merged in feat/cloud-gate-foundation, all still 1101):\n1. Added `--yes` to the deploy step so the React-based Plan UI is skipped (this fixed a separate Symbol.toPrimitive bug; deploy now reaches the upload phase)\n2. Pass full assets config object {directory, config:{notFoundHandling, htmlHandling, runWorkerFirst}} to mirror apps/docs/wrangler.jsonc — alchemy default for bare-string `assets:` was wrong\n3. Pass `isExternal: true` to skip alchemy bootstrap that otherwise wraps `main` in `Layer.effect(tag, entry).asEffect()` — OpenNext exports plain `{ fetch }` shape\n\nBundle size from the upload phase: 40.62 MB unminified. CF Workers Standard limit is 10 MB compressed; 40 MB raw is at the edge.\n\nLikely root cause (untested): rolldown bundle of OpenNext.worker.js inlines the dynamic `import(\"./server-functions/default/handler.mjs\")` instead of preserving as a chunk, producing one oversized file that workerd refuses to fully load. wrangler deploy handles this differently (preserves chunks).\n\nPossible directions:\n- Split via rolldown output options on alchemy side (would require alchemy upstream change)\n- Use a Build resource pattern to feed alchemy a pre-built bundle and skip rolldown entirely\n- Leave docs on plain wrangler deploy (revert alchemy.run.ts for docs) until alchemy ships native OpenNext support\n\napps/web deploys via Cloudflare.Vite which uses viteBuild instead of prepareBundle — that path works.","notes":"Update 2026-04-24 (got CF token with workers tail scope from himitsu cloudflare-api-token):\n\nCaptured the original 1101 root cause via tail:\n No such module \"node:perf_hooks\". imported from \"handler-BwC-NBMH.js\"\n\nFixed by bumping compatibility_date to 2026-03-17 (commit on feat/cloud-gate-foundation). That date is when CF promoted node:perf_hooks to a native module — earlier dates make unenv try to polyfill it, but the polyfill itself does `import \"node:perf_hooks\"` so it cant substitute itself in a chunked bundle.\n\nAfter fix: docs.stackpanel.com homepage returns 200, but /docs/* routes return 500 with a different exception:\n Failed to load external module shiki-db8d315635eb368c/core\n\nRoot cause (different bug): rolldown bundles props.main but Cloudflare Worker config has `unresolvedImport: false` (alchemy-effect/src/Cloudflare/Workers/Worker.ts:715). When OpenNext’s middleware/handler imports its own pre-bundled chunks like `shiki-db8d315635eb368c/core`, rolldown silences the unresolved-import warning, leaves the import literal, and the deployed worker fails to dynamic-import that module name at runtime.\n\nWorkaround paths:\n- Drop alchemy for apps/docs and revert to `wrangler deploy` (the previous wrangler.jsonc approach). Trade-off: lose declarative cert/DNS, but unblocks docs.\n- Patch alchemy/distilled.cloud rolldown plugin to NOT silence unresolved imports (would surface the issue at build time) and bundle shiki inline.\n- Pre-process .open-next/ output before passing to alchemy so all chunks are inlined into a single file.\n\nRecommend option 1 for now and revisit when alchemy adds a no-bundle / pass-through mode for `main`.","status":"open","priority":2,"issue_type":"bug","owner":"me@cooperm.com","created_at":"2026-04-25T03:20:41Z","created_by":"Cooper Maruyama","updated_at":"2026-04-25T04:08:54Z","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-i5r","title":"Studio: Marketplace browse + install panel","description":"In-studio UI for discovering, purchasing, and installing modules.\n\n## Scope\n\n- apps/web/src/components/studio/panels/marketplace-panel.tsx\n- Sections: Featured, Official (stackpanel's own paid modules), Community, Installed\n- Listing detail view: README, versions, pricing, screenshots (MDX + images from the listing)\n- Install button: free → instant; paid → Polar checkout in a popup, webhook-driven refresh on return\n- Installed view: updates available, usage (if module reports it), remove\n- Calls into the CLI via agent-local endpoints for the actual install/uninstall (so studio doesn't need Nix directly)","acceptance_criteria":"- Browse renders paginated list with search\n- Listing detail shows full MDX description + pricing\n- Free install works without leaving the studio\n- Paid install flow completes end-to-end without manual reload","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:13Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:13Z","dependencies":[{"issue_id":"stackpanel-i5r","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:09Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-i5r","depends_on_id":"stackpanel-w3r","type":"blocks","created_at":"2026-04-23T20:46:10Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-24e","title":"Revenue accounting: gross / fee / developer net ledger","description":"Ledger that tracks every transaction with platform fee + developer share; source of truth for payouts and future reporting.\n\n## Scope\n\n- Drizzle table: revenue_event(id, license_id FK, event_type enum(purchase|renewal|refund|chargeback), gross_cents, fee_cents, developer_net_cents, currency, polar_event_id unique, occurred_at)\n- Derived view: developer_balance(user_id, module_slug, balance_cents, last_updated) — sum of developer_net_cents minus already-paid-out\n- Populated by the same Polar webhook handler as the license writes\n- Dashboard API: /api/me/revenue — current balance, monthly breakdown, per-module totals\n\n## Rules\n\n- Platform fee: 15% flat at MVP, calculated at write time (can change later without rewriting history)\n- Processing fee (~3%) absorbed from the 15% — developer always gets 85% of gross minus refunds\n- Refund: negative revenue_event, reduces balance\n- Chargeback: negative + lock payout temporarily (resolution flow is Phase 2)","acceptance_criteria":"- Every Polar webhook produces exactly one revenue_event (idempotent)\n- developer_balance view reconciles to sum of events\n- Refunds correctly decrement balance\n- Dashboard API returns accurate per-developer totals","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:08Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:08Z","dependencies":[{"issue_id":"stackpanel-24e","depends_on_id":"stackpanel-p4y","type":"blocks","created_at":"2026-04-23T20:46:08Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":4,"comment_count":0}
+{"id":"stackpanel-w3r","title":"CLI: stackpanel install + Nix module resolver integration","description":"End-user install flow that adds a module to .stack/config.nix and wires it through the existing module system.\n\n## Scope\n\n### CLI (apps/stack-go)\n- stackpanel install \u003cslug\u003e[@version] — looks up in catalog, checks license (for paid), fetches signed tarball, unpacks to .stack/modules/\u003cslug\u003e/\u003cversion\u003e/, edits .stack/config.nix to add the module reference + version pin\n- stackpanel uninstall \u003cslug\u003e — removes pin; leaves unpacked tarball for potential rollback\n- stackpanel update \u003cslug\u003e — checks catalog for new versions, prompts to upgrade\n- Handles paid flow: if no license, opens Polar checkout URL in browser, waits for webhook to create license, then continues install\n\n### Nix integration\n- New option: stack.modules.install = [ { slug = '...'; version = '...'; source = './path or fetchTarball'; } ];\n- Resolver: if source points to .stack/modules/\u003cslug\u003e/\u003cversion\u003e/, import the module's module.nix and merge into config\n- Auto-detection from .stack/modules/ dir as fallback\n\n## Why CLI-first\n\nStudio panel can come later — CLI covers CI + power users today.","acceptance_criteria":"- stackpanel install \u003cfree-slug\u003e works end-to-end\n- stackpanel install \u003cpaid-slug\u003e launches checkout, completes install post-purchase\n- Installed module's options appear under stack.modules.\u003cslug\u003e in the config\n- stackpanel uninstall cleanly reverts config.nix","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:00Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:00Z","dependencies":[{"issue_id":"stackpanel-w3r","depends_on_id":"stackpanel-89x","type":"blocks","created_at":"2026-04-23T20:46:07Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-w3r","depends_on_id":"stackpanel-qij","type":"blocks","created_at":"2026-04-23T20:46:08Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-qij","title":"Module tarball signing + verification","description":"Ed25519 signatures on module tarballs so installers can verify authorship + integrity without trusting the distribution CDN.\n\n## Scope\n\n### Publishing side (dev portal)\n- Module author uploads tarball; cloud API signs it with platform key (MVP) OR author brings own signing key (Phase 2 with KYC)\n- Store signature + public key id in module_version.signature\n- Tarball hosted on R2/S3 with signed URLs\n\n### Verification side\n- Agent/CLI: on stackpanel install, fetch manifest + tarball, verify signature against catalog's published public key\n- Refuse install on signature mismatch with clear error\n- Nix side: evaluate tarball as a flake input with 'narHash' pin (Nix's own integrity check) once extracted — belt + suspenders\n\n## Why signing even with HTTPS\n\n- Defends against CDN compromise or MITM on corporate proxies\n- Enables offline-verifiable attestation of who published what\n- Precondition for any automated-update flow","acceptance_criteria":"- All published versions have a valid signature\n- CLI install refuses on signature mismatch (tested with a tampered tarball)\n- Signature check adds \u003c 500ms to install flow","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:44:51Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:44:51Z","dependencies":[{"issue_id":"stackpanel-qij","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:06Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-89x","title":"Module license + workspace entitlements","description":"License record granting a workspace the right to install a specific module (+ seats).\n\n## Scope\n\n- Drizzle tables:\n - module_license(id, listing_id FK, workspace_id FK, polar_subscription_id | polar_order_id, status enum(active|canceled|expired|refunded), seats int default 1, activated_at, expires_at nullable)\n - module_license_event(id, license_id FK, event_type, polar_event_id unique, payload_json, created_at) — audit log\n- tRPC:\n - modules.listLicenses(workspace_id) — what this workspace owns\n - modules.verifyLicense(slug, workspace_id) → { valid, reason?, expires_at? } (called by CLI + agent)\n- Capability JWT emission (reuses stackpanel-0bt infra): when agent requests a capability token, include 'modules': [{slug, version_pin}] in claims for all active licenses\n- License inheritance across workspace forks / preview stages — TBD, default to no inheritance\n\n## Enforcement touchpoints\n\n- stackpanel install \u003cslug\u003e — CLI checks license before adding pin\n- Shell entry — agent re-verifies licenses on devshell entry for installed paid modules; warns on lapsed\n- Module-provided cloud endpoints — receive capability JWT, check 'modules' claim","acceptance_criteria":"- License auto-created on Polar webhook\n- verifyLicense correctly reflects active/expired/refunded states\n- Capability tokens include per-license claims\n- stackpanel install refuses without a valid license","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:44:43Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:44:43Z","dependencies":[{"issue_id":"stackpanel-89x","depends_on_id":"stackpanel-0bt","type":"blocks","created_at":"2026-04-23T20:46:05Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-89x","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:03Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-89x","depends_on_id":"stackpanel-9uo","type":"blocks","created_at":"2026-04-23T20:46:05Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-89x","depends_on_id":"stackpanel-p4y","type":"blocks","created_at":"2026-04-23T20:46:04Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":4,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-p4y","title":"Polar integration: module products + checkout","description":"Wire each paid module's pricing to a Polar product; buy → checkout → webhook creates license.\n\n## Scope\n\n- When a module pricing row is created with model != 'free': auto-create a Polar product via @polar-sh SDK (one_time → product without interval; subscription → product with interval). Store polar_product_id in module_pricing.\n- tRPC procedure: modules.checkout(slug, workspace_id) → returns Polar checkout URL. Called by studio/CLI.\n- Polar webhook handler (packages/api/src/routes/webhooks/polar.ts):\n - subscription.active / order.paid → create module_license row\n - subscription.canceled / order.refunded → mark license inactive\n - Idempotent on Polar event id\n- Success redirect to studio marketplace panel with a 'Installed' flow\n\n## Notes\n\n- Separate Polar products per module so fee accounting is clean and developers can see per-product revenue in Polar dashboard once they're onboarded\n- Use Polar test env for dev; wire real keys via .stack/secrets/vars/common.sops.yaml","acceptance_criteria":"- Free modules skip Polar entirely\n- Paid module checkout returns a working Polar URL\n- Purchase → webhook → license row appears within 10s\n- Refund → license marked inactive; install command refuses","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:44:34Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:44:34Z","dependencies":[{"issue_id":"stackpanel-p4y","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:03Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-63e","title":"Module catalog: Drizzle schema + tRPC search/list","description":"Foundation data model for the marketplace.\n\n## Scope\n\n- Drizzle tables:\n - module_listing(id, slug, name, summary, description_md, author_user_id, status enum(draft|pending|approved|rejected|deprecated), category, created_at)\n - module_version(id, listing_id FK, version semver, tarball_url, signature, manifest_json, released_at)\n - module_pricing(listing_id FK, model enum(free|one_time|subscription), price_cents, interval enum(month|year|null), polar_product_id, active bool)\n- tRPC router packages/api/src/routers/modules.ts\n - modules.list({ category?, q?, cursor? }) — public, paginated\n - modules.get(slug) — public, returns listing + latest version + pricing\n - modules.listVersions(slug) — public\n- Slug validation + reserved-word list (prevent 'stack', 'stackpanel', 'official', etc. without admin flag)\n- Seed script with 3 stub listings for local dev\n\n## Why first\n\nEvery other marketplace task depends on this catalog existing.","acceptance_criteria":"- Drizzle migration applied cleanly\n- modules.list returns paginated results with search\n- modules.get returns a full listing with pricing + latest version\n- Reserved slugs cannot be used outside admin","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:44:27Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:44:27Z","dependency_count":0,"dependent_count":6,"comment_count":0}
+{"id":"stackpanel-b86","title":"Premium modules marketplace","description":"Epic: enable third-party developers to publish paid modules to the extension registry; users install/purchase from the studio or CLI; revenue flows to the developer minus a platform fee.\n\n## Scope\n\n- Catalog API, listings, search (free + paid in one lane)\n- Polar-backed checkout for one-time + subscription pricing\n- Per-workspace licensing with team seats\n- Signed module tarballs, verification at install/shell-entry\n- Developer portal for submitting and managing listings\n- Revenue accounting + payout rails (Polar Connect / Stripe Connect)\n- Manual review workflow at MVP; automated Nix static analysis later\n- Studio 'Marketplace' browse panel; 'stackpanel install \u003cmodule\u003e' CLI\n- Integrates with existing stack.modules option + nix/stack/modules/ auto-discovery\n\n## Economics\n\n- 15% platform fee (developer receives 85% net of processing)\n- Processing (~3%) absorbed from the 15%, not the developer's share\n- USD first; multi-currency later via Polar\n- Monthly payout minimum (TBD — $50? $100?)\n\n## Trust \u0026 safety\n\n- Nix modules can execute arbitrary build code. MVP = curated launch partners + manual review + signed tarballs required.\n- Phase 2 = automated static analysis for IFD, impure builtins, network calls outside known registries\n- Phase 3 = restricted 'pure' DSL for modules that want a verification badge\n\n## Relationship to other work\n\n- Depends on stackpanel-9uo (protectedPaidProcedure middleware) — reuses the same gate pattern\n- Benefits from stackpanel-0bt (capability JWT) for shell-entry license checks\n- Module catalog shares DB with @stack/db; uses same Polar + Better-Auth plumbing\n\n## Open questions (resolve before Phase 1 ships)\n\n1. Platform fee: 15% sticker, or tiered (higher cut for top sellers / lower for new devs)?\n2. Price floor/ceiling on one-time purchases?\n3. Annual subscription discount rate?\n4. Refund policy — 14-day hard return, or author discretion?\n5. Licensing across workspace forks + preview deploys — each needs its own seat or shared?\n6. Stackpanel's own paid modules (Hosted State, AI) — same catalog or separate 'Official' tier?\n\n## Phasing\n\n- **Phase 1 (MVP)**: curated launch, ~5 partner modules, manual payouts, honor-system + signature verification\n- **Phase 2**: self-serve dev portal, Polar Connect payouts, usage-metered pricing, automated static analysis\n- **Phase 3**: restricted DSL, plugin quality badges, bundled 'collections' pricing","acceptance_criteria":"- Developers can publish a paid module via dev portal and receive payouts\n- Users can discover, purchase, and install paid modules from studio + CLI\n- Per-workspace licensing enforced; non-licensed workspaces cannot install paid modules\n- Revenue accounting tracks gross, platform fee, and developer net\n- Signed tarballs verified at install; unsigned/invalid → refuse\n- Curated launch with ≥3 partner modules shipped","status":"open","priority":2,"issue_type":"feature","owner":"me@cooperm.com","created_at":"2026-04-24T03:44:16Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:44:16Z","dependencies":[{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-02c","type":"blocks","created_at":"2026-04-23T20:46:30Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-24e","type":"blocks","created_at":"2026-04-23T20:46:28Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-3vi","type":"blocks","created_at":"2026-04-23T20:46:31Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:24Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-89x","type":"blocks","created_at":"2026-04-23T20:46:26Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-c7t","type":"blocks","created_at":"2026-04-23T20:46:29Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-i5r","type":"blocks","created_at":"2026-04-23T20:46:29Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-l1q","type":"blocks","created_at":"2026-04-23T20:46:31Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-p4y","type":"blocks","created_at":"2026-04-23T20:46:25Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-qij","type":"blocks","created_at":"2026-04-23T20:46:26Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-tvv","type":"blocks","created_at":"2026-04-23T20:46:32Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-b86","depends_on_id":"stackpanel-w3r","type":"blocks","created_at":"2026-04-23T20:46:27Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":12,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-0bt","title":"Capability JWT: issuance endpoint + agent-side verifier","description":"Short-lived signed JWT from cloud API embedding plan claims, verified by Go agent before unlocking gated commands.\n\n## Scope\n\n### Cloud side (packages/api)\n- POST /api/capability/issue → returns { token, expiresAt }\n- Payload: { sub: userId, workspace_id, plan, seats, exp: now+1h, iss: api.stackpanel.com }\n- Signed with Ed25519; public key checked into repo + embedded in agent binary\n- Private key in AWS SSM (follows existing secret pattern)\n\n### Agent side (apps/stack-go)\n- New middleware capability.Verify that reads X-Stackpanel-Capability header\n- Verifies Ed25519 signature against embedded public key\n- On success: injects plan claims into request context\n- On expiry/invalid: 401 with clear error\n\n### Studio side (apps/web)\n- AgentProvider fetches capability token on login, refreshes 5min before expiry\n- Passes as header on every agent request\n\n## Scope note\n\nThis is defense in depth. Primary gating is server-side (the tRPC middleware). Capability JWT matters only for local-only features we'll gate later (e.g., Pro UI panels).","acceptance_criteria":"- Pro-tier user successfully obtains and refreshes capability tokens\n- Agent rejects requests without valid capability on gated endpoints\n- Token expiry triggers clean refresh (no visible interruption in studio)\n- Agent's embedded public key loaded at build time, verified with a unit test","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:23Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:41:23Z","dependencies":[{"issue_id":"stackpanel-0bt","depends_on_id":"stackpanel-9uo","type":"blocks","created_at":"2026-04-23T20:42:09Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-9dq","title":"HostedStateStore adapter for alchemy-effect","description":"Implement alchemy-effect's StateStore interface backed by the cloud tRPC router.\n\n## Scope\n\n- packages/infra/src/state/HostedStateStore.ts\n- Implements the StateStore contract from alchemy-effect/State (get/put/list/delete)\n- Uses @trpc/client with httpBatchLink pointed at api.stackpanel.com, Bearer auth via ALCHEMY_STATE_TOKEN\n- Retries (Effect.retry with exponential backoff) for transient 5xx, not for 4xx\n- Structured error mapping so alchemy's UX shows 'Subscription required' on 402 instead of a cryptic stack trace\n- Feature flag via env: STACKPANEL_STATE_BACKEND=hosted|local; default local\n\n## Integration\n\n- apps/web/alchemy.run.ts and apps/docs/alchemy.run.ts: conditionally construct HostedStateStore when STACKPANEL_STATE_BACKEND=hosted","acceptance_criteria":"- Adapter passes alchemy-effect's StateStore contract tests\n- 402 from API surfaces as actionable 'upgrade' error in alchemy CLI output\n- Can deploy apps/web end-to-end with STACKPANEL_STATE_BACKEND=hosted","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:14Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T07:34:27Z","closed_at":"2026-04-24T07:34:27Z","close_reason":"Closed","dependencies":[{"issue_id":"stackpanel-9dq","depends_on_id":"stackpanel-ehz","type":"blocks","created_at":"2026-04-23T20:42:08Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":4,"comment_count":0}
+{"id":"stackpanel-ehz","title":"tRPC router for alchemy state CRUD","description":"New tRPC router exposing state operations to the HostedStateStore adapter.\n\n## Scope\n\n- packages/api/src/routers/alchemyState.ts\n- Procedures (all wrapped with protectedPaidProcedure):\n - get(stack, stage, fqn) → decrypted JSON | null\n - put(stack, stage, fqn, payload, expectedVersion?) → new version (optimistic concurrency)\n - list(stack, stage?) → [{fqn, version, updated_at}]\n - delete(stack, stage, fqn) → void\n - listStages(stack) → [{stage, resource_count, updated_at}]\n- Input validation with Zod (stack/stage/fqn slug regex to prevent injection)\n- Error mapping: 409 on version mismatch, 404 on missing, 402 on unsubscribed (from middleware)\n- Wire router into packages/api/src/routers/_app.ts","acceptance_criteria":"- All procedures callable from a tRPC client with a Pro subscription\n- Version conflict returns 409 (not 500)\n- Unsubscribed caller gets 402\n- Integration test exercises put→get→list→delete round-trip","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:09Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T07:34:27Z","closed_at":"2026-04-24T07:34:27Z","close_reason":"Closed","dependencies":[{"issue_id":"stackpanel-ehz","depends_on_id":"stackpanel-9uo","type":"blocks","created_at":"2026-04-23T20:42:06Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-ehz","depends_on_id":"stackpanel-9zb","type":"blocks","created_at":"2026-04-23T20:42:07Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-9zb","title":"Drizzle schema for workspace_state with envelope encryption","description":"DB schema + KMS integration for storing alchemy state entries, encrypted per-workspace.\n\n## Scope\n\n- Drizzle migration: workspace_state(id uuid, workspace_id uuid FK, stack text, stage text, fqn text, key_id text, encrypted_blob bytea, version int, updated_at timestamptz). Unique on (workspace_id, stack, stage, fqn).\n- New table workspace_dek(workspace_id FK PK, encrypted_dek bytea, kms_key_id text, created_at) — per-workspace DEK wrapped by master KMS key\n- Helpers in @stack/db: encryptStateBlob(workspaceId, plaintext) / decryptStateBlob(workspaceId, ciphertext) using @aws-sdk/client-kms\n- Lazy DEK creation on first write; DEK rotation hook (not wired to a schedule yet)\n\n## Notes\n\n- Plaintext state MUST NOT be persisted anywhere. Decrypt on read, discard after response.\n- Master KMS key already provisioned via @stackpanel/infra — reuse that key ID.","acceptance_criteria":"- Migration applied cleanly to a fresh dev DB\n- encryptStateBlob/decryptStateBlob round-trip correctly\n- workspace_dek row auto-created on first encryptStateBlob call\n- KMS decrypt calls only happen on read; no plaintext persisted","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:02Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T07:34:27Z","closed_at":"2026-04-24T07:34:27Z","close_reason":"Closed","dependency_count":0,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-9uo","title":"Add protectedPaidProcedure middleware in @stack/api","description":"Middleware that wraps protectedProcedure and additionally verifies the user has an active Polar subscription at the 'pro' tier or higher. Rejects with TRPCError code FORBIDDEN (HTTP 402) + upgrade_url when the check fails.\n\n## Scope\n\n- packages/api/src/lib/middleware/paid.ts: new Effect-backed middleware\n- Reads subscription state via the existing @polar-sh/better-auth plugin\n- Passes {workspace_id, plan} into ctx for downstream procedures\n- Unit tests covering: active pro, active free, trial, expired, no session\n\n## Why this first\n\nUnblocks every cloud-gated feature we'll ever build — Hosted state is just the first caller.","acceptance_criteria":"- protectedPaidProcedure exported from @stack/api\n- Free/expired users get 402 with { code, upgrade_url }\n- Pro users pass through with plan claims in ctx\n- Tests cover all subscription states","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:40:55Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T07:34:27Z","closed_at":"2026-04-24T07:34:27Z","close_reason":"Closed","dependency_count":0,"dependent_count":4,"comment_count":0}
+{"id":"stackpanel-e7v","title":"Hosted alchemy state backend (Pro tier)","description":"Epic: Build a hosted state backend for alchemy-effect (replacing the filesystem-only backend), gated behind a Pro subscription via Polar. Unlocks team deploys and fixes CI orphaning.\n\n## Motivation\n\nToday alchemy-effect ships only a filesystem state backend (noted in .github/workflows/deploy-{web,docs}.yaml). Our CI has to cache the .alchemy/state/ directory across runners so destroy jobs can find the resources to tear down — otherwise 'every runner starts blind and orphans Workers + Neon projects' (comment from deploy-web.yaml).\n\nThis is:\n- A real pain felt today (CI workaround in place)\n- Gate-able by architecture (requires our cloud API; cannot be reproduced locally)\n- Natural fit with existing Polar + Better-Auth + Neon + KMS plumbing\n- Already half-scaffolded: ALCHEMY_STATE_TOKEN is in packages/gen/env/src/effect/scope/deploy.ts\n\n## Strategy\n\n- Cloud API (api.stackpanel.com) exposes tRPC procedures for state CRUD, gated by a Polar subscription check\n- Per-workspace envelope encryption (KMS master key wraps a per-workspace DEK)\n- Custom StateStore adapter on the alchemy-effect side that talks to the cloud API\n- .stack/config.nix option to toggle hosted vs local backend\n- Studio UI panel showing state entries per stage\n\n## Free vs Pro\n\n- **Free**: local filesystem backend (status quo). Works fine for solo use.\n- **Pro**: hosted backend. Survives CI runner churn. Enables true team deploys. Audit log of who deployed what.","design":"## Architecture\n\n- Alchemy-effect StateStore is an interface (see alchemy-effect/State module). We implement HostedStateStore that calls tRPC procedures via HTTPS.\n- tRPC routers live in packages/api/src/routers/alchemyState.ts with a protectedPaidProcedure middleware gate.\n- DB schema (Drizzle): workspace_state(id, workspace_id, stack, stage, fqn, encrypted_blob, key_id, version, updated_at).\n- Encryption: per-workspace DEK stored encrypted by KMS master key. On read, API decrypts DEK via KMS, decrypts state blob, returns plaintext. Plaintext never persisted.\n- Auth: ALCHEMY_STATE_TOKEN is a signed capability JWT issued by cloud API after Better-Auth login, embedding { workspace_id, plan, exp }. Agent-side verifier checks plan claim before allowing mutations (defense in depth).\n- CI: workflows pass ALCHEMY_STATE_TOKEN (from GitHub secret) → adapter uses it as Bearer → API verifies signature + plan.\n\n## Subscription gate\n\nprotectedPaidProcedure middleware:\n1. Resolve session via Better-Auth\n2. Look up Polar subscription via @polar-sh/better-auth plugin\n3. If plan.tier \u003c 'pro' or status !== 'active' → TRPCError 402 FORBIDDEN with upgrade_url\n4. Otherwise pass {workspace_id, plan} in ctx to procedure","acceptance_criteria":"- Pro users on Polar-active subscriptions can deploy using hosted state backend\n- Non-subscribers get 402 from cloud API with clear upgrade link\n- CI deploys no longer need the filesystem cache workaround in deploy-{web,docs}.yaml\n- State is encrypted at rest (no plaintext blobs in Postgres)\n- Studio has a 'State' panel showing deployed stages + resources per workspace\n- Docs guide covers migration from local → hosted","status":"open","priority":2,"issue_type":"feature","owner":"me@cooperm.com","created_at":"2026-04-24T03:40:31Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:40:31Z","dependencies":[{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-0bt","type":"blocks","created_at":"2026-04-23T20:42:24Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-3p8","type":"blocks","created_at":"2026-04-23T20:42:25Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-3qt","type":"blocks","created_at":"2026-04-23T20:42:26Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-6a3","type":"blocks","created_at":"2026-04-23T20:42:26Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-9dq","type":"blocks","created_at":"2026-04-23T20:42:23Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-9uo","type":"blocks","created_at":"2026-04-23T20:42:21Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-9zb","type":"blocks","created_at":"2026-04-23T20:42:21Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-bni","type":"blocks","created_at":"2026-04-23T20:42:24Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-e7v","depends_on_id":"stackpanel-ehz","type":"blocks","created_at":"2026-04-23T20:42:22Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":9,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-7zb","title":"Move .envrc generation into lib.initFiles (drop Go constant)","description":"stackpanel init currently hardcodes .envrc contents in a Go const (envrcContent in apps/stackpanel-go/cmd/cli/init.go). This duplicates nix/flake/templates/default/.envrc and means the template can drift from what ship-to-user init writes.\n\nAction: add .envrc to lib.initFiles in nix/flake/exports.nix so it flows through the existing stepWriteInitFiles pipeline as a single source of truth; remove envrcContent + stepGenerateEnvrc from init.go; keep the idempotency semantics (don't clobber existing .envrc unless --force).\n\nContext: during stackpanel-y53/0qu work (2026-04-23), the agent chose to keep .envrc on the Go side because lib.initFiles appeared broken. It was not — lib.initFiles works and already ships .stackpanel/{config,_internal,data,.gitignore}. It just doesn't include .envrc today, and its templateDir path (../flake/templates/default/.stackpanel) points at a nonexistent dir while the real templates live under nix/flake/templates/default/.stack and the repo-root .envrc. Fix that path drift too while you're in there.","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-23T16:08:51Z","created_by":"Cooper Maruyama","updated_at":"2026-04-23T16:08:51Z","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-y53","title":"add envrc generation to 'stackpanel init'","status":"closed","priority":2,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-04-23T15:35:17Z","created_by":"Cooper Maruyama","updated_at":"2026-04-23T15:45:55Z","started_at":"2026-04-23T15:37:04Z","closed_at":"2026-04-23T15:45:55Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-0qu","title":"stackpanel init should use 'gum' to guide user through setup with a nice tui. it shold be idempotent such that when new steps are added, users can just run it again.","status":"closed","priority":2,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-04-23T15:34:56Z","created_by":"Cooper Maruyama","updated_at":"2026-04-23T15:45:56Z","started_at":"2026-04-23T15:37:05Z","closed_at":"2026-04-23T15:45:56Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-7hn","title":"agent should become a systemd or launchd service and service restarts. it should also support multiple projects at once, currently its one agent per project. by passing the absolute path to the project in every request, it could support multiple projects. with a project ID sent in payloads to reference projects","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-20T23:27:23Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T23:27:23Z","comments":[{"id":"019dbb02-49e6-7163-b828-e4ce2e90cc43","issue_id":"stackpanel-7hn","author":"Cooper Maruyama","text":"agent should probably still run the way it does not via process-compose in development, rather than launchd","created_at":"2026-04-23T15:43:03Z"}],"dependency_count":0,"dependent_count":0,"comment_count":1}
+{"id":"stackpanel-0h5","title":"agent should publish a public api that allows you to do the same things you can do via the cli","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-20T23:25:16Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T23:25:16Z","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-b8l","title":"rewrite proto nix lib so that defining defaults/descriptions isnt so awkward. dont force the positional API - allow an API which would be the new default that feels just like the canonical lib functions like lib.mkOption except they accept a proto index","status":"closed","priority":2,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-04-20T22:51:57Z","created_by":"Cooper Maruyama","updated_at":"2026-04-23T15:47:42Z","started_at":"2026-04-23T15:37:05Z","closed_at":"2026-04-23T15:47:42Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-30y","title":"env-package: stale src/runtime/generated-payloads/registry.ts duplicates src/generated-payloads/registry.ts","description":"The Nix env-package emits a stub at `packages/gen/env/src/runtime/generated-payloads/registry.ts` (via `payloadRuntimeDir = \"${runtimeDir}/generated-payloads\"` in env-package.nix), while the Go codegen writes the real registry to `packages/gen/env/src/generated-payloads/registry.ts` (`generatedPayloadsRoot` constant in apps/stackpanel-go/internal/codegen/env.go).\n\nThe runtime imports use `./generated-payloads/registry` relative to `src/runtime/`, so they resolve to the stale Nix stub. Go codegen never overwrites it; only the sibling `src/generated-payloads/registry.ts` reflects the true per-app/env payload registry.\n\nThis is a pre-existing inconsistency (predates the multi-env fan-out work) but should be fixed: either drop the runtime/ subpath in env-package.nix and update loader/node-loader imports, or move Go codegen output under runtime/.","status":"closed","priority":2,"issue_type":"bug","owner":"me@cooperm.com","created_at":"2026-04-17T18:05:44Z","created_by":"Cooper Maruyama","updated_at":"2026-04-17T19:16:51Z","closed_at":"2026-04-17T19:16:51Z","close_reason":"Fixed: aligned Go generatedPayloadsRoot to packages/gen/env/src/runtime/generated-payloads so Go overwrites the Nix stub as designed. Orphaned src/generated-payloads/ tree removed; regeneration verified end-to-end.","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-foe.6","title":"P6: Studio UI + Docs","description":"Expose deploy specs + state through agent API (read-only). Update Studio Deploy panel to consume backend-agnostic deploy specs and state. Add deploy/provision action triggers from panel. Unify deployment docs around pluggable backend model. Document how to create a new backend module (contributor guide).","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:35Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:39:35Z","dependencies":[{"issue_id":"stackpanel-foe.6","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:35Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.6","depends_on_id":"stackpanel-foe.4","type":"blocks","created_at":"2026-03-28T13:47:45Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-foe.5","title":"P5: Dogfood on nixmac","description":"Add stackpanel deploy config to ~/git/darkmatter/nixmac. Deploy nixmac web via fly — validate generated fly.toml matches existing hand-written one. Prove the module system works in a project that isn't stackpanel.","status":"open","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T20:39:34Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:39:34Z","dependencies":[{"issue_id":"stackpanel-foe.5","depends_on_id":"stackpanel-foe","type":"parent-child","created_at":"2026-03-28T13:39:33Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-foe.5","depends_on_id":"stackpanel-foe.4","type":"blocks","created_at":"2026-03-28T13:47:44Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-rvr.4","title":"Audit and eliminate remaining non-tree-sitter config mutation paths","description":"Use the current audit as a checklist and eliminate remaining .stack/config.nix mutation paths that still depend on eval-\u003eJSON-\u003eserialize rewrites where path-level AST edits should be used instead.\n\nAcceptance criteria:\n- Document every remaining .stack/config.nix write path and classify it as tree-sitter patch, intentional raw write, or migration target\n- Convert any remaining fine-grained config mutation paths that should preserve source structure to tree-sitter patching\n- Leave only intentional full-file writes or YAML-backed secrets writes in the exceptions list\n- The final audit is captured in code comments, docs, or issue updates closely enough that the repo does not regress to eval-and-reserialize edits by accident","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T16:34:04Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:47Z","closed_at":"2026-04-20T22:37:47Z","close_reason":"No remaining non-tree-sitter config mutation paths for secrets. Tree-sitter/flakeedit only used for flake.nix inputs, not secrets.","dependencies":[{"issue_id":"stackpanel-rvr.4","depends_on_id":"stackpanel-rvr","type":"parent-child","created_at":"2026-03-28T09:34:04Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-rvr.3","title":"Rewire Studio secrets UI to use YAML-backed APIs","description":"Update the Studio secrets screens so they stop calling useNixEntityData(\"secrets\") for settings that are now YAML-backed.\n\nAcceptance criteria:\n- The recipient/groups/KMS secrets UI reads and writes through the new YAML-backed secrets APIs\n- Saving from Studio no longer updates .stack/config.nix for Studio-managed secrets settings\n- User-facing copy in the secrets UI reflects the YAML/SOPS-backed workflow instead of saying values are saved to config.nix\n- Relevant UI tests or focused verification cover the updated save flow","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T16:34:03Z","created_by":"Cooper Maruyama","updated_at":"2026-04-20T22:37:40Z","closed_at":"2026-04-20T22:37:40Z","close_reason":"Studio secrets UI uses agentClient.readSopsSecrets/writeSopsSecret/deleteSopsSecret via /api/sops/* endpoints.","dependencies":[{"issue_id":"stackpanel-rvr.3","depends_on_id":"stackpanel-rvr","type":"parent-child","created_at":"2026-03-28T09:34:02Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-rvr.3","depends_on_id":"stackpanel-rvr.2","type":"blocks","created_at":"2026-03-28T09:34:07Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-os2.7","title":"Unify deployment docs and migration guides around the current model","description":"The repo currently mixes older provider-centric deployment docs (for example nix/stackpanel/deployment/README.md and apps/docs/content/docs/internal/deployment.mdx) with newer Nix-first deploy/provision design docs. Once the remaining CLI/backend work lands, refresh the public/internal docs so users see one coherent deployment story instead of parallel models.","design":"Document the shipped behavior after the CLI/backend/UI work settles; keep examples aligned with docs/design/deploy-command.md and docs/design/provisioning.md.","acceptance_criteria":"- Public docs cover app deploy, machine-target deploy, provisioning, and hosted-backend flows with current commands/options\n- Internal/module docs stop teaching superseded provider/defaultProvider shapes where they no longer match shipped behavior\n- Migration guidance explains old vs new config shapes and current container/deploy commands\n- Examples and validation steps match actual commands in the repo","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:38Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:30Z","closed_at":"2026-03-28T20:19:30Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-381","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.7","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:38Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.7","depends_on_id":"stackpanel-os2.3","type":"blocks","created_at":"2026-03-28T08:02:42Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.7","depends_on_id":"stackpanel-os2.4","type":"blocks","created_at":"2026-03-28T08:02:42Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.7","depends_on_id":"stackpanel-os2.5","type":"blocks","created_at":"2026-03-28T08:02:43Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.7","depends_on_id":"stackpanel-os2.6","type":"blocks","created_at":"2026-03-28T08:02:43Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":4,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-os2.5","title":"Add stackpanel provision --new and config round-trip machine authoring","description":"apps/stackpanel-go/cmd/cli/provision.go handles provisioning for machines that already exist in config, but the provisioning design also calls for a --new workflow that can author a minimal machine entry and preserve Nix path literals for hardwareConfig/diskLayout updates. Add that machine-authoring path so new-machine setup is not a manual edit-before-provision step.","design":"Reuse the repo's existing config-writing/serialization patterns instead of inventing a new config mutator; add tagged path handling if necessary to preserve Nix path types.","acceptance_criteria":"- stackpanel provision --new \u003cname\u003e --host \u003ctarget\u003e creates a minimal machine entry in the canonical Stackpanel config\n- hardwareConfig and diskLayout paths round-trip as Nix path literals instead of quoted absolute strings\n- The provision flow can update the new machine entry after generating hardware config\n- Add tests for config edit / serialization behavior","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:37Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:21Z","closed_at":"2026-03-28T20:19:21Z","close_reason":"Dropped: manual config editing is acceptable, provision --new deferred indefinitely","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-382","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.5","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:36Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.5","depends_on_id":"stackpanel-os2.1","type":"blocks","created_at":"2026-03-28T08:02:40Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-os2.6","title":"Wire deploy/provision state into the Studio Deploy panel","description":"apps/web/src/components/studio/panels/deploy/deploy-panel.tsx is still Colmena-centric and does not appear to consume the CLI state tracked in .stack/state/deployments.json and .stack/state/machines.json. Update the Studio deploy experience so it reflects the same deploy/provision model and status that the CLI writes.","design":"Expose deploy/provision state through the agent/web API rather than teaching the browser to read local state files directly.","acceptance_criteria":"- The Deploy panel shows machine provisioning state and last deploy state from the supported agent/CLI APIs\n- Users can trigger deploy/provision actions from the panel with clear loading, success, and error states\n- Unsupported or partially configured backends degrade gracefully in the UI\n- Add frontend or integration coverage for the key panel states","status":"closed","priority":2,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-03-28T15:02:37Z","created_by":"Cooper Maruyama","updated_at":"2026-03-28T20:19:29Z","closed_at":"2026-03-28T20:19:29Z","close_reason":"Superseded by pluggable-deploy-backends restructure. Work absorbed into new phase-based tasks. See openspec/changes/pluggable-deploy-backends/","external_ref":"https://linear.app/darkmatterlabs/issue/ENG-383","labels":["deployment"],"dependencies":[{"issue_id":"stackpanel-os2.6","depends_on_id":"stackpanel-os2","type":"parent-child","created_at":"2026-03-28T08:02:37Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.6","depends_on_id":"stackpanel-os2.3","type":"blocks","created_at":"2026-03-28T08:02:40Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.6","depends_on_id":"stackpanel-os2.4","type":"blocks","created_at":"2026-03-28T08:02:41Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-os2.6","depends_on_id":"stackpanel-os2.5","type":"blocks","created_at":"2026-03-28T08:02:41Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":3,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-zhq","title":"Remove now-obsolete actions/cache@v4 of apps/{web,docs}/.alchemy from deploy workflows","description":"In the alchemy-effect → alchemy@2 migration (PR #16), all 5 deploy stacks switched from filesystem-based LocalState to Cloudflare-hosted state via Cloudflare.state(). The .alchemy/state/ directory is no longer used at deploy time.\n\nThe deploy workflows still cache it as a no-op:\n\n .github/workflows/deploy-web.yaml — Restore alchemy state (actions/cache@v4 on apps/web/.alchemy)\n .github/workflows/deploy-docs.yaml — Restore alchemy state (actions/cache@v4 on apps/docs/.alchemy)\n destroy job — actions/cache/restore@v4 of the same paths\n destroy job — Delete cached alchemy state (gh cache delete) cleanup\n\nPlus the explanatory comment block above each cache step describing the LocalState pattern is now misleading.\n\nCleanup:\n- Drop the cache@v4 + cache/restore@v4 steps from both workflows\n- Drop the gh cache delete cleanup step in the destroy jobs\n- Update or remove the now-misleading 'Persist alchemy's LocalState' comment blocks\n- Verify deploy still works without the cache (the Cloudflare state store is the new source of truth and is self-bootstrapping per Cloudflare.state())\n\nShould land after Cloudflare.state() is verified working in CI (depends on stackpanel-r7g / PR #16).","status":"open","priority":3,"issue_type":"chore","owner":"me@cooperm.com","created_at":"2026-04-29T09:17:13Z","created_by":"Cooper Maruyama","updated_at":"2026-04-29T09:17:13Z","dependencies":[{"issue_id":"stackpanel-zhq","depends_on_id":"stackpanel-r7g","type":"discovered-from","created_at":"2026-04-29T02:17:13Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-49t","title":"Restore .open-next/cache asset overlay once alchemy@2 supports AssetsProps.sources","description":"In the alchemy-effect → alchemy@2 migration (PR #16), we deleted the vendored OpenNext asset overlay (vendor/alchemy-effect-opennext-overlay/, scripts/apply-alchemy-effect-opennext-assets.ts, root postinstall hook) because:\n\n1. It was tied to alchemy-effect@0.12.x's file structure and is incompatible with v2's restructured Worker.ts/Assets.ts\n2. The script's own self-disable path explicitly instructs maintainers to delete the hook when no alchemy-effect@0.12.x installs are found\n\napps/docs/alchemy.run.ts had its assets.sources field commented out with a TODO referencing this issue:\n\n assets: {\n directory: '.open-next/assets',\n // TODO(stackpanel): re-enable the .open-next/cache overlay once\n // alchemy@2 natively supports AssetsProps.sources …\n config: { … },\n }\n\nImpact: OpenNext incremental cache misses for cdn-cgi/_next_cache paths fall back to ISR revalidation. Cache hit-rate regression, not a hard breakage.\n\nscripts/ALCHEMY_EFFECT_OPENNEXT_UPSTREAM.md (also deleted) tracked the upstream PR for AssetsProps.sources support — verify whether it landed in alchemy@2's main branch and is just pending a release, or whether it needs to be re-pitched.\n\nResolution path:\n- Option A: wait for upstream alchemy to ship native AssetsProps.sources, then uncomment the field in apps/docs/alchemy.run.ts\n- Option B: re-vendor a v2-compatible overlay (risky — alchemy@2 has restructured Worker.ts/Assets.ts internals)\n- Option C: switch to a different OpenNext cache strategy that doesn't require the overlay\n\nAcceptance: docs deploy serves cdn-cgi/_next_cache assets from Workers Assets directly (Option A or B), or this issue is closed as won't-fix with a documented alternative.","status":"open","priority":3,"issue_type":"feature","owner":"me@cooperm.com","created_at":"2026-04-29T09:17:00Z","created_by":"Cooper Maruyama","updated_at":"2026-04-29T09:17:00Z","dependencies":[{"issue_id":"stackpanel-49t","depends_on_id":"stackpanel-r7g","type":"discovered-from","created_at":"2026-04-29T02:17:00Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
+{"id":"stackpanel-3vi","title":"Docs: module author guide + marketplace policies","description":"Docs that make it obvious how to build, test, price, and publish a module — plus the policies that keep the marketplace trustworthy.\n\n## Scope\n\n### Author guide (apps/docs/content/docs/modules/)\n- 'Build your first module' — scaffolding, module.nix structure, meta.nix fields, ui.nix if applicable\n- 'Test a module locally' — stackpanel link (local dev), running against sample .stack/config.nix\n- 'Package for publication' — tarball layout, signing, manifest requirements\n- 'Price and publish' — free vs paid tradeoffs, pricing UX tips\n- 'Get paid' — Polar Connect onboarding, tax docs, payout schedule\n- 'Versioning + updates' — semver discipline, deprecation policy\n\n### Policies\n- Acceptable use: no crypto miners, no telemetry without disclosure, no license keys hardcoded\n- Refund policy: 14-day no-questions-asked (author can opt into stricter)\n- Takedown policy: security issues → emergency delist within 24h\n- Revenue share + fee structure (the 15% sticker, transparent)\n- Intellectual property: developer retains ownership, grants distribution license","acceptance_criteria":"- Author guide builds with apps/docs\n- Policies are linked from dev portal's publish flow\n- Sample module repo referenced from the 'first module' page","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:46Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:46Z","dependencies":[{"issue_id":"stackpanel-3vi","depends_on_id":"stackpanel-02c","type":"blocks","created_at":"2026-04-23T20:46:16Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-3vi","depends_on_id":"stackpanel-c7t","type":"blocks","created_at":"2026-04-23T20:46:15Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-3vi","depends_on_id":"stackpanel-w3r","type":"blocks","created_at":"2026-04-23T20:46:17Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":3,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-l1q","title":"Module review workflow + automated Nix static analysis","description":"Prevent malicious or broken modules from reaching users. MVP manual, Phase 2 automated.\n\n## Scope\n\n### MVP: manual review\n- Admin tool (packages/api route + studio admin panel) showing pending listings\n- Reviewer sees: uploaded tarball contents, diff from previous version (if any), links to GitHub repo, automated scan results\n- Approve → listing goes live; Reject → listing status updated with reason visible to author\n- SLA target: 3 business days for initial review\n\n### Phase 2: automated scans\n- Static-analysis pass over module.nix + meta.nix:\n - Flag: import-from-derivation without explicit opt-in\n - Flag: builtins.fetchurl with non-allowlisted host\n - Flag: arbitrary path reads outside module dir\n - Flag: network calls during eval\n- Feed findings into review UI; author sees them pre-submit\n- Optionally: automatic 'verified pure' badge for modules with zero findings\n\n## Why not AI review\n\nPattern-match is more reliable for this than an LLM for the boring 'did they try to phone home during eval' checks. LLM review can come later for README/security claims.","acceptance_criteria":"- Reviewer can approve/reject pending listings\n- Rejected listings show reason to author with re-submit path\n- Static analysis surfaces known-bad patterns in a handful of test cases","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:37Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:37Z","dependencies":[{"issue_id":"stackpanel-l1q","depends_on_id":"stackpanel-c7t","type":"blocks","created_at":"2026-04-23T20:46:15Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-02c","title":"Developer payout: Polar Connect + KYC onboarding","description":"Pay developers their accrued balance via Polar Connect (Stripe Connect underneath), with KYC + tax form collection at onboarding.\n\n## Scope\n\n- Onboarding flow: first time creating a paid listing → prompt to connect Polar Connect account (redirect OAuth flow)\n- Collect tax info (W-9 US / W-8BEN international) via Polar's Connect UI\n- Payout job (scheduled): once per month, for each developer with balance \u003e= $50, trigger Polar payout; record payout_event(developer_id, amount_cents, polar_transfer_id, status)\n- Emails: onboarding done, first sale, monthly statement\n- Admin tool for manual payout holds (fraud, chargeback disputes)\n\n## Phase 1 fallback\n\nIf Polar Connect isn't ready: accumulate balances, issue manual Wise transfers quarterly while we collect via email. Works for ~20 developers, not for scale.","acceptance_criteria":"- Developer can connect payout account end-to-end\n- Monthly payout runs successfully against test Polar env\n- Balance decrements match transferred amount\n- Tax forms captured before first payout","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:28Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:28Z","dependencies":[{"issue_id":"stackpanel-02c","depends_on_id":"stackpanel-24e","type":"blocks","created_at":"2026-04-23T20:46:13Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-02c","depends_on_id":"stackpanel-c7t","type":"blocks","created_at":"2026-04-23T20:46:14Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-c7t","title":"Developer portal: submit + manage listings","description":"Dashboard where authors create listings, upload versions, set pricing, view revenue.\n\n## Scope\n\n- apps/web/src/routes/studio/developer/* (new section gated to users with \u003e0 listings or who opt in)\n- Create listing wizard: slug, name, summary, category, repo link (optional)\n- Upload version: drag-drop tarball, auto-extract manifest, show diff from previous version\n- Pricing editor: choose free / one-time / subscription; set price; connect Polar product (auto-created by backend)\n- Revenue dashboard: balance, recent transactions, export CSV\n- Listing status flow: draft → pending review → approved/rejected → published\n- Reject reasons visible to author with actionable next steps\n\n## MVP alternative\n\nIf this feels too big for Phase 1: start with a Google Form + manual backend entry. Still gets to ~5 launch partners. Ship dev portal when we have \u003e10 authors waiting.","acceptance_criteria":"- Author can create a listing, upload a version, set pricing, and submit for review\n- Rejected listings show the reason; author can fix and resubmit\n- Revenue dashboard reconciles with backend ledger","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:20Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:20Z","dependencies":[{"issue_id":"stackpanel-c7t","depends_on_id":"stackpanel-24e","type":"blocks","created_at":"2026-04-23T20:46:12Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-c7t","depends_on_id":"stackpanel-63e","type":"blocks","created_at":"2026-04-23T20:46:11Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-c7t","depends_on_id":"stackpanel-qij","type":"blocks","created_at":"2026-04-23T20:46:12Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":3,"dependent_count":4,"comment_count":0}
+{"id":"stackpanel-3qt","title":"Docs: hosted alchemy state backend — guide + migration","description":"Documentation for the hosted backend: tradeoffs, setup, and migration from local.\n\n## Scope\n\n- apps/docs/content/docs/guides/hosted-state.mdx: new guide\n- Cover: why hosted (team deploys, CI resilience, audit trail), why not (solo use, offline), subscription requirement, encryption model, incident/outage behavior\n- Migration steps: toggle Nix option → obtain ALCHEMY_STATE_TOKEN → first deploy uploads existing local state\n- Screenshots of the Studio State panel\n- Link from deploy-*.yaml workflow comments to this doc\n\n## Why bundle this with the feature\n\nUsers won't discover hosted state unless it's in the docs; Pro conversion depends on understanding the value.","acceptance_criteria":"- Guide builds with apps/docs\n- Links from workflow comments + from the Studio empty state\n- Covers pricing + how the capability token works","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:46Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:41:46Z","dependencies":[{"issue_id":"stackpanel-3qt","depends_on_id":"stackpanel-3p8","type":"blocks","created_at":"2026-04-23T20:42:15Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-3qt","depends_on_id":"stackpanel-9dq","type":"blocks","created_at":"2026-04-23T20:42:15Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-3qt","depends_on_id":"stackpanel-bni","type":"blocks","created_at":"2026-04-23T20:42:16Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":3,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-6a3","title":"Migrate CI deploys off filesystem cache once hosted backend ships","description":"Kill the actions/cache hack in deploy-{web,docs}.yaml.\n\n## Scope\n\n- .github/workflows/deploy-web.yaml: remove 'Restore alchemy state' step (lines ~82-93)\n- .github/workflows/deploy-docs.yaml: remove analogous step (lines ~80-88)\n- Same for the destroy jobs\n- Add ALCHEMY_STATE_TOKEN to the job env (pulled from GitHub secrets)\n- Update comments: explain why state persistence is no longer our problem\n- Verify preview deploys still work (state now survives runner churn because it's in our DB)\n\n## Blocker\n\nOnly run this after HostedStateStore is shipped and proven on dev-CI for a week.","acceptance_criteria":"- CI workflows have no filesystem state caching\n- Preview deploys + destroys work across runner churn\n- bun.lock / workflow diff shows net-negative lines","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:41Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:41:41Z","dependencies":[{"issue_id":"stackpanel-6a3","depends_on_id":"stackpanel-9dq","type":"blocks","created_at":"2026-04-23T20:42:11Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-6a3","depends_on_id":"stackpanel-bni","type":"blocks","created_at":"2026-04-23T20:42:14Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0}
+{"id":"stackpanel-3p8","title":"Studio: State panel showing hosted state per workspace/stack/stage","description":"New studio UI panel for browsing hosted state — who deployed what, when, which resources exist per stage.\n\n## Scope\n\n- apps/web/src/components/studio/panels/state-panel.tsx\n- Wires to alchemyState.list + listStages tRPC procedures (not the agent)\n- Tree view: workspace → stack → stage → resources\n- Row actions: 'View JSON' (decrypt on-demand via get), 'Rollback to previous version' (requires versioning; keep as a follow-up)\n- Empty state for local-backend users pointing to the Pro upgrade\n\n## Why this matters\n\nGives Pro users immediate visible value — audit log-like view of their cloud state they couldn't have on local filesystem.","acceptance_criteria":"- Panel lists all stages for the active workspace\n- JSON view decrypts on demand and renders in a modal\n- Local-backend users see upgrade prompt\n- Panel doesn't break for free users (no 500s)","status":"open","priority":3,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:35Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:41:35Z","dependencies":[{"issue_id":"stackpanel-3p8","depends_on_id":"stackpanel-ehz","type":"blocks","created_at":"2026-04-23T20:42:10Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}
+{"id":"stackpanel-bni","title":"Nix option: stack.deploy.stateBackend = 'hosted' | 'local'","description":"Expose the state backend choice in .stack/config.nix so users can opt into the Pro backend declaratively.\n\n## Scope\n\n- nix/stackpanel/core/options/deploy.nix: new option stack.deploy.stateBackend (default 'local')\n- When 'hosted': emit STACKPANEL_STATE_BACKEND=hosted into _envs/deploy, and mark ALCHEMY_STATE_TOKEN as required\n- When 'local': leave current behavior untouched\n- Preflight warning if 'hosted' but no ALCHEMY_STATE_TOKEN in scope\n- JSON schema update for IDE intellisense (.stack/gen/schemas/)","acceptance_criteria":"- Config validates with stateBackend = 'hosted' + ALCHEMY_STATE_TOKEN present\n- Missing token emits a clear preflight warning\n- Default-unchanged behavior: existing users see no difference","status":"closed","priority":3,"issue_type":"task","assignee":"Cooper Maruyama","owner":"me@cooperm.com","created_at":"2026-04-24T03:41:28Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T07:36:45Z","started_at":"2026-04-24T07:34:36Z","closed_at":"2026-04-24T07:36:45Z","close_reason":"Closed","dependencies":[{"issue_id":"stackpanel-bni","depends_on_id":"stackpanel-9dq","type":"blocks","created_at":"2026-04-23T20:42:09Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":1,"dependent_count":3,"comment_count":0}
+{"id":"stackpanel-tvv","title":"Phase 2: usage-metered pricing via Polar Meters","description":"Enable modules to charge by usage (e.g., 'N deploys/month', 'M agents run') in addition to flat pricing.\n\n## Scope\n\n- Polar Meter integration via @polar-sh SDK\n- Module manifest gains optional 'meters' field declaring the usage dimensions\n- Runtime: module reports usage via tRPC modules.reportUsage(slug, meter, delta) — gated by license\n- Billing: Polar rolls up usage monthly; webhook events translate into revenue_event rows\n- Author-side: dashboard shows usage graphs + projected revenue\n- User-side: studio shows current usage with soft caps before hard billing\n\n## Why Phase 2\n\nOne-time + subscription covers 95% of modules. Metered billing adds real complexity (rate limits, reconciliation, disputes). Ship it when we have a use case demanding it.","acceptance_criteria":"- At least one test module uses metered pricing end-to-end\n- Usage events reconcile between our ledger and Polar\n- User can see current usage in studio","status":"open","priority":4,"issue_type":"task","owner":"me@cooperm.com","created_at":"2026-04-24T03:45:52Z","created_by":"Cooper Maruyama","updated_at":"2026-04-24T03:45:52Z","dependencies":[{"issue_id":"stackpanel-tvv","depends_on_id":"stackpanel-24e","type":"blocks","created_at":"2026-04-23T20:46:18Z","created_by":"Cooper Maruyama","metadata":"{}"},{"issue_id":"stackpanel-tvv","depends_on_id":"stackpanel-89x","type":"blocks","created_at":"2026-04-23T20:46:18Z","created_by":"Cooper Maruyama","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0}
diff --git a/packages/gen/env/data/_envs/deploy.sops.json b/packages/gen/env/data/_envs/deploy.sops.json
index 92dea3a2..dc969562 100644
--- a/packages/gen/env/data/_envs/deploy.sops.json
+++ b/packages/gen/env/data/_envs/deploy.sops.json
@@ -1,90 +1,90 @@
{
- "ALCHEMY_STATE_TOKEN": "ENC[AES256_GCM,data:rTBiBapZCBJFCuXzp8f6VezyB3mNfwONjBU=,iv:qZWr5xm3G3pUMkWGQ7SX4jeui6qxIR3aqecUTbBvTEo=,tag:GBXJiGBpq5/Hx5MkZIO0Uw==,type:str]",
- "AWS_SANDBOX_ACCESS_KEY_ID": "ENC[AES256_GCM,data:FtAfQI0e9lubjlhRiuZDLkkzdYA=,iv:GHg/HPyKqWxQVnHB7+CljxO5wdMWNEQhSU/6gsUPyZE=,tag:AiXojokdTW2sqs2BP7VWzA==,type:str]",
- "AWS_SANDBOX_SECRET_ACCESS_KEY": "ENC[AES256_GCM,data:KS5MH2XWo2cJ8luY9fgiuy2ws8+yPvJnz7PHthjllfQbMbFPji8rZg==,iv:cNlBBQ1n1WI11TBfMGGL3iRcj9OczeFWyeNwdmJgvrk=,tag:rG4mmfJBHjyVXQ7H5vn6eg==,type:str]",
- "BETTER_AUTH_SECRET": "ENC[AES256_GCM,data:Ak2jq5yT+bwfJ5Y5xC9ehRZimpvRKu8yG+KowhKY/t+wqrErA6XNFSPjeQY=,iv:6BAT/PGqkNDmOJxvgNm3FcZOHLMw8Dyk8wpZCUQIAO0=,tag:ihaG3P7ANyKYBZTG4asNoQ==,type:str]",
- "CLOUDFLARE_ACCOUNT_ID": "ENC[AES256_GCM,data:OFw5coSME91vXXYBX6ffTEDtw0K05M/An+NDl3MEF4k=,iv:gQOIkTkFOUUDlQ5bZExGMdfUkLXpdqsLW3JM89HHohw=,tag:iYCJ3yHiGp7FozCQoUknRg==,type:str]",
- "CLOUDFLARE_API_TOKEN": "ENC[AES256_GCM,data:tHZuQsE380/tDNwfVUO1h1Loo2BuueSuCsr84OkZ6ZvNg3PO22cfDl6wIWu/JWIZM6WLehU=,iv:Go3pv2JOk8kYtSLyYpWWco3bKaFCECEPXQ4clAAB5ak=,tag:KQvOu7gISqs881hgLc8G1Q==,type:str]",
- "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_ID": "ENC[AES256_GCM,data:IU4XL8j3GbYk2DiBw+P7B7VYdP8p2UGKoBItAR5L1XSgzEfPKCCa,iv:4gutigTJknDBUvoDmvMvOWs1GHXLDhVeJGyAE/37Gcg=,tag:tBb9GuOftO12pDP6kVf6gw==,type:str]",
- "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_SECRET": "ENC[AES256_GCM,data:AsArdfpbL0O/Ojkt0Q2i9GuJ+hKotFNhoVr2RMGDrbcG3WKBFdr/zhMUVULlZlakvmiK3+MDMTpGxol01lr3UA==,iv:9FFKl+bAu2C+71zlRbuVJKA5LMq9PXUdGsrbWIsP++0=,tag:/7wbmQqyYNyODgmMW87JWQ==,type:str]",
- "HETZNER_API_KEY": "ENC[AES256_GCM,data:dG36TPkWVtb6qVa7BIE3UF/fKJnviSWGy/WzKHSRVSmLdtOhp3LYuDs34abNtT/1ZQRduE77sAYbDlNYLx/rLQ==,iv:TBpn0Npv19x+2F69BFWZUPsahT5BzSFHN03o9U8iYEI=,tag:BWN6qOFScqSu0Okcik4CuA==,type:str]",
- "NEON_API_KEY": "ENC[AES256_GCM,data:HolPRRDTnvsXbwXPF09rNXVJhw9UQF1s26IS0WolWGo5scVg6LA2LlIQqQlI+AZSuEClZ9axP4qhSJigrqxiwhKhOdWv,iv:1Ye1rSMUyVZRzUca/k0Kh0zYRJGJxubj8JHmfkwPUHE=,tag:Fl6PzPUBZ7pPFxDf/FB6Lw==,type:str]",
- "POLAR_ACCESS_TOKEN": "ENC[AES256_GCM,data:8B1tMabKQBBf9pVPv5dm+Vh100dHui2JqJsPiIoU9Bno9ikS7jU=,iv:rA13nuouvQ+X8oIi2wKv+gebP/6OGk/frtqtbh48oN0=,tag:Leu4q/yuI7SIdqIzaYIvfg==,type:str]",
- "POLAR_FREE_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:vBnCCRo+OKLbWbhsSmW6gisSI5AvsAdY,iv:Kx0zkQK1IZS6aQoDRJ1lSZM/DUZbkXSquhH1HGPPK1g=,tag:fc+wD/KV54d9AFmOKVTOkQ==,type:str]",
- "POLAR_PRO_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:LMKgcw214sz/gXo2T6h00h4S7u2Aiak=,iv:J+RunAALWPeK6j8z8RTDoX+rckalVE4AFltWmuD0km4=,tag:T4nc5+nKC+wEsV17dld3mw==,type:str]",
- "POLAR_WEBHOOK_SECRET": "ENC[AES256_GCM,data:VLXmN4UWFjJjVDeh2nA5OpDyuAiBBqFK4GaFmOHgpKl/Ww==,iv:17PUD90QslYrtx2H4Y9vNrlHgiw3PjQz/G9uhpVeRPc=,tag:8e4uZdupkWm2xSTXuFHX1Q==,type:str]",
- "POSTGRES_URL": "ENC[AES256_GCM,data:THuclyynG8PyTlVVQM0aaCRCgfGcNRCV8OJbZuhLhMswuFXz9KJakGiVCa/tfGXelpTcoTB+edIS/g8k3GqOTBt3ii67KUQEK5XX0ChW++IcR6g3y8m1cq6aiGqEx9EXYZyv2VqEv5dJFN7JonQtbiobpkpU722JyHwdcOzPOfqTio+3HBzVKw+BOQMRMPAH0haPj/ggeiVojQMra3PyeenMAwk+BJngzg==,iv:M3M+/f76EW4yux9DGZv+RmUV4uvXQTMbfD0kMbB2nEY=,tag:C5aQgNgGzmfiHl/Iq13Xdw==,type:str]",
- "STACKPANEL_API_URL": "ENC[AES256_GCM,data:TzF0BYY0DX78f7UllHGj4iaTzE/MSLV15wo=,iv:0uDDBFCigDYkPPCXPOF8gVz7GkD7u3ECzHlU52rt72c=,tag:Izgidk9pFMPLCMAQgm1pEA==,type:str]",
- "STACKPANEL_STATE_BACKEND": "ENC[AES256_GCM,data:03Q3pxs=,iv:MBCDQ2yBJUM/ytwAoTmEt+Q5KB2dKcH5UQ5g9AvxCYE=,tag:os/gN98UCeJiweIGtbIfEg==,type:str]",
+ "ALCHEMY_STATE_TOKEN": "ENC[AES256_GCM,data:GBcVz2EiFOvIIih333Lw06ixU/rpMfBO210=,iv:7U9U0ZT3csEhINsSold/cnTCDXnmbxFVYJXvyPDHOnM=,tag:Cn64WbbWWtXPk8f1kLot4A==,type:str]",
+ "AWS_SANDBOX_ACCESS_KEY_ID": "ENC[AES256_GCM,data:Jx8DQgBTwMqj7pTj25oNRRw+yvk=,iv:YXmcb/rf8LmP3S++emsugIn6Ruxcy0uYqLaGlg/D13k=,tag:mbjfPPcFwkUwZCrY68757w==,type:str]",
+ "AWS_SANDBOX_SECRET_ACCESS_KEY": "ENC[AES256_GCM,data:odETLJwgoP4ygcxl53+p4syxfOo6jjTf9tXmF4Fi5G77+W+FvUhOjQ==,iv:vfUPoM585zkANi2EJaO1p8sB5tb0VfdSdNXyZr7Hevk=,tag:qqUkgmkFJw9JoYdASABVpg==,type:str]",
+ "BETTER_AUTH_SECRET": "ENC[AES256_GCM,data:CGzPpiRyq2bQddUcuULEqAE7HVd7ae8pkpEBK9S4B7h6UalHfP16gftvaxM=,iv:T4WpjIdpfKkRyTc60wDKBvqMezGSsBRejV19X5zIxis=,tag:48iKQt4JKp+svpTW91SU3A==,type:str]",
+ "CLOUDFLARE_ACCOUNT_ID": "ENC[AES256_GCM,data:qUHUaEAT3kCYWXHWJDvLNFWe4hxGH9i3J27X/0oCxto=,iv:yFO2whpGJHTXu4SX+IBDC+QF0/FyPvZKfVJZY/PyMAU=,tag:+HbNzS+IgkWuK2m0LAWx3g==,type:str]",
+ "CLOUDFLARE_API_TOKEN": "ENC[AES256_GCM,data:kwuguFkv40mnCnnbtr6sqaF0tH0gg4xrFDjn5LSc2rpnebAschpu6xA0rs0EX+i0ieN6UZs=,iv:yJVrAal8GkhKOqMVYR+r6IGtH7tElsf3Oue9JSQIDBQ=,tag:C8GfQFNsWk6b+RgWQClTQA==,type:str]",
+ "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_ID": "ENC[AES256_GCM,data:g3zfDSaX4cw9KI6J4YJbObN+BLkEbYeSrmkqaaU1bnInC2/w61UG,iv:mdZdgGY6JFFK2R3k+5MTPN394Rdwl4qWD6P7bvqMRLY=,tag:+5qQn4LQq3RJlf0iNIwFJg==,type:str]",
+ "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_SECRET": "ENC[AES256_GCM,data:NEZPMzorh2YchykdkZ4zDGDKcmishp1XphdDLrr39Ca00H04DKCmUNIr8RLBS2QQSmb4pVrdU2kAGnr+on6xvg==,iv:sSp3COVynQkSK/VNje1O5wehuhfTQRYKdknVey0UGqM=,tag:bBuvfxkXT+2ysjogC8iU1A==,type:str]",
+ "HETZNER_API_KEY": "ENC[AES256_GCM,data:j2ofslGsxLUki6b08NxpGZjcInBUlLd3U8IL8FyOP92BGBpdMGa0bky9V3kQeHdyXxX83B2a4+dyg+SFZnlJMQ==,iv:+G1vlPOCQ5fVMoaluSkpOaHClcUWzNbawsZyE5lx1h8=,tag:oXnyowIVoSG8Fgswod1AkA==,type:str]",
+ "NEON_API_KEY": "ENC[AES256_GCM,data:UtdrbQjpxO569cnvx1YpFJbrpbd/KnYB8caj1iAReABKpJthTzSux2zd9Z9RN4Inp2zmhLWjRQ5WejWfYZDClzNTWRf1,iv:lijmT6sZI3R5++NDVPctRF5TGdBbncxA8+byDGSccKo=,tag:9Y3+QL1kc1CeRhmvacRfUA==,type:str]",
+ "POLAR_ACCESS_TOKEN": "ENC[AES256_GCM,data:3onI0xX7R1kCR5u2IaESrmGfhFGo5R2kKT/b6qDTnsBHSsqZItQ=,iv:pZi5xTotR0Ho9y4e94Q3XXUnuGUe+/2wt7yz1JcqExI=,tag:l46niw1WC/07x3BbkNwVkA==,type:str]",
+ "POLAR_FREE_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:6w7FBOGFK0o9FBRlioZTZptElWYtZ6g8,iv:9XNuy0MTEn1upFWqbvpLeeBOgIpK1Lhu9nZpxu3ZMtA=,tag:mgMxm8iaSvepafA9H6FaTA==,type:str]",
+ "POLAR_PRO_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:G9VpESvGAXaC/aYtERIOdoNHMpbVkzw=,iv:dN/yu71QOrKekJ2upYK72SBAjr+ui/JMZaZOLnjBgGU=,tag:hqcfP9z2satrqOq0K6giCA==,type:str]",
+ "POLAR_WEBHOOK_SECRET": "ENC[AES256_GCM,data:d5fYyac8z3t85qHL7Y0eDyggBXTABrLmpnqX0GOFry7JRA==,iv:betPLe/SFjoQPW41ffCFeGUXoKlY9K6zDVSU2hjmD04=,tag:cXYfpje4yppB7VvrNHb4gw==,type:str]",
+ "POSTGRES_URL": "ENC[AES256_GCM,data:35yZq2/My43JOB79D8IY2YOMV+ICkF/d8dYVg3ig193tCUWW8tdL+8/KOX5RTD1vmcRW0acXfN7inGx1SaOKAZnbU0tizHYXAHziDfrMmRnIq5T++Lt5ZZ0FdiuzSPAvpYsvPPaaI5mH7L9tzK4iovfXMC8bn6ktKYPmoZByiZ99jAWj3qPQxUh4KLMxaAi+O5gX8em2jsY06ErVVldoqzs2eyKCUNJG5Q==,iv:re0opsnqllbKCCoYfzyZuMmPi7YY6cJHKS7MS6V8OMc=,tag:wJgzC+GblqQnf29pnYQLaA==,type:str]",
+ "STACKPANEL_API_URL": "ENC[AES256_GCM,data:cZs6KnGRIKTxAZ48pDahAyJu8OUYUVHjPn0=,iv:qEPiZt17oIqE+XKe97QdlJlpxXwcb4x6m6811qYCr1U=,tag:29HIaBBYVcD5GtJ2v2TgMw==,type:str]",
+ "STACKPANEL_STATE_BACKEND": "ENC[AES256_GCM,data:W5giOY0=,iv:SM6ubBsxMcSNFRr1kaSif9ktsKYDVqo8BtaQVxBRryU=,tag:iGUVhdiRWiR86drWokVJMA==,type:str]",
"sops": {
"age": [
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICjMrEzKQSq/0eePzgUGXM349sBm6GVCXoz+tJZJaBVT",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHZMYWFYZyB3RFpi\nNnR0MndOdVlaZDdXT2oyZ3U4YjR6SXFhN01RLzJtQnZtZFJSTG4wClAvTFJYNjlp\nVy9ITlRmWkhGTEhRc2tTT3ZiN0FrRU9SQmNVZDFxOGNqd00KLS0tIGVzSkphdEZa\nenc0L0Vpa1p5VXRUOXRVV3pMUUNRT0k1L0dLL2l0SHoxeEkKBrzAJ6XZD8PEE2Ut\nWi8TBrllT1qYs+wtbNA8T9iZF1ifndkjRdxxZ0EkWsdypOw297XIfKPQsqajIe5c\nmwdnlA==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHZMYWFYZyB3Yk9t\nUUlhZWRhVitScklxMjhRbGswRy94L04wWDA1bG80NXZPVVFDL2lVCkZGVHJIN1Vh\nNUpXN21mU09OWGNUYVJYREw5azJ5NnpyYllDK1FxTXRFVTQKLS0tICs0dEFWcU43\nbkN2ODd1U3NDT1EyTElWNFZqLzhVQTN2ejlOZURDZENNQ1EKG5Xb/q1q4uZn5VcE\nMRHUMyIgL4WLHdsH/JjecTyeMB9foM7njshWx3eKh+Ln4/nyoUle/wrVzkHucggG\nup9lRQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIoX0wKjybps6ZHuvA+uwE1ThfWl87MApDLxCjCDUCC9",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEtlVWxBdyBFK1lV\nNTdlMU1NenpmMkZUY2dmbnpKUkpHSkRJRHZ1MDV6NTJTb00xMFQwCmdGcW9TS1NQ\nN3A1b3kwNXhUMHd6VzRJMjI0NHArdXJ5ck1vQmE0S0pDL1EKLS0tIEk4TGJ4ZTNF\ndGR3bDhCNzJNMjVZbW5IUlZnT0ZPcWd3UlR0NVFCQkdHKzQK+nefxkP3XX8keweE\ntUB85kQv9AYnIP7LDv16cBa17RCtOSaDI1cRjEGw3K+skLffOKxR4d8l/715+2ou\nHMfpGQ==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEtlVWxBdyBnU1d2\nRFZRaXBmUVJvaWFENFRVMlJ4WWV3MVJzemg2bkM3amFFUHp5Z1ZFCm91T1ZUNmVl\ncmJZVCtHazlsRzBVRFpzYisySGRjTUZKbkdrU1FPWHFEMG8KLS0tIE4xWEpPSmlB\nTG10MDNYNFNHbkxLbUFacDQrYmI0OStTKzViSDFnMnh2UkEKD8NQFopgJycqwRTQ\nPDZVo7uMG2wJYW+F76puwSlgWN1udYOTzgrYwPlvIOcJs53Coz53jCNlGys2APQK\n4sSWDw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGbPiUycu2bZdvvd+b4b2OtjSMMcIhH/lmedaMnye0BM",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGcrY0ZCUSA3NTFo\nZE11YithNitEZXRRWDdNekFXcFhzN2YrZlFSRTN4eGZsTEpITXc4CjR1dzVwQmVC\nV2ZkM0h0Yk1JMHJsVDhhanYwT2JvSTZnTE55YTh5SzVHYWsKLS0tIDRybGdXeXhZ\nT1BEQzVqVHJPcCsycmJ3c0FISmpBU0QyL3VBcUR5STdoNkkKp/CDw996X/FDhTIL\nmeMdVN5kL41zaRHTrSpmN8bhUG/0eL7QP9vurTpz24P6Dk1qvGBjjhhcqvRW9WIf\n5yVsCw==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGcrY0ZCUSByZ3ht\ncW1kRzk4WVdGbThsTzdyampFUTlVVmZFWXRTUHg0cjFXUXJtc0JVCnlZYlZYUzc0\nSHlSejRaOFpOaDRlRTlqc3JtR242RFgva2JiUnowWCs2aDgKLS0tIHlXZnNVcFRD\nMEI5aE5oNkZ4OGZsb2ZmRDFaOTBac2VnS29ralYrNmZ6U28KWuoTRneXf4VCsO4e\n7la4d4ZkuYx7DeBWoG14+oZzgcWGrBXWhGIyc6DZL/z4mwQ0xhZ2qkKIWLih5BsG\nwhTznA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKZPhznHjTDifGroHiGRD3hwWz69B8NsSSPNbwWjfzgW",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDFNdXZydyBYR1A3\ndkFLZ1N1emovV2g0c0VadXZFL0dxaXlKUHk4V1ZPQzBPOXVnWndJCmpXc0J1TmZD\na3l4SmhJWmZEUjUwRGlxNTI5RDVUcjRwTWllZlNXRVQxZ2cKLS0tIFJLb0g1K2FJ\na2xXRzV1WnozMjd1WGU1UmVmWW9RdmVhOFpqdUxvN05HakEKhiisF8PSRNG7klo1\npj5gjVX04D2n7AEzNaTO9ccvz55d/NYN7AJUy2/HIpVc+3am6FY0oCzmP/jwsYd2\n1K2FuA==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDFNdXZydyBybUVm\neHV1QUV1OVo1UEdJRW9YYjNOejA1cnd0RHF0eTlEdVhRMXBoZEJ3Ckd2NnFSQ0lF\nZTMyMmtWbkt3Zm81NVl2Q1p4dWl2cEJyUlZsU3ppRWFSZnMKLS0tIFludFZhKzY1\nVnBwSzFPaVVWR1gzWjNySmFhZ3lWek9FU3czMnRxL0cybWsKgtBwibqNpljgZtMy\n7tvND6kgQiSCoGe0kvMSfDcRaIDvh/oCjbu5/vr7s40oXZKKo+b6Uzjbfx02qh5F\n0GkxUg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJF9kzudl00FVTQA7nnSaicAJW2MoBKm47G7wefb6uSm",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGw3N2NtUSBqZlJi\nb3dvZjR3ekVPVzl1R09XbWRQUkNURWJqN3lQdURHWUsvUS9sR2tBCndtUkd6azRu\nWC9sTFJOb3JwR0RLMkVYdU50SDgyNGRoZjFtNzFNMUdnY0UKLS0tIHVWbVluQTBB\nSmtGeTFjMW0vWGIrbVZlQlM3alBBM0tRUTBabWJISlRnOU0KWcDcyXU9K0wxKIzr\njQtFSSPQDsgd0Tur/cBK41ikxn6/BcdVBCgE6zsllTvP79sm/sTdslq5q2OZGUJv\nbwg0Eg==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGw3N2NtUSA2cnhP\nNFNWQ29KczhteVBkbytuU1hXdzhkdjVTNXAzay84NFF5dXNDakdjCkRxUlhDdHIr\ncDFnL2NGQkxGWnk2bENCa2pqQXVXNFl6cVBsbGxNNncxMkkKLS0tIFd0d0NjdmhY\nUHZMZEZqT3RDTndXN1FkdU95MXZRbHFiSU1RWVY5OFdYc0kKmH2MSGIb4KrEkySo\nuXngFJs4G0qUOZ20pJlOlI8o/WD6si3huRDXJC720p0ePldlsASwE1nidX3oTjBf\ncO/8Bg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDmZV9Oi9hrckXTpqBnDKRrY9mgwj+SJeVBGWmne2+q5",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpUeE1RUSBncGVD\nNHlZY0FuTjJhWTNTcS9xdlY0bEtyNjFyWXQ3Z3RadEtHdFNaanhJClhldmQ2a2h0\nRkZXTkNhZnhScjgweWZ1bndmNFpBbmZ0WnAyYmMxbkdSNDgKLS0tIHdpOHFWb0sr\naitlVnNUNENacjBlZmt5dWtHK05iRXFjeWthNHRVL0t1ZzQKGpBxMEoqm44l9E4U\nOzEMFrwh/r4I4XDE7x3G4dlZVEuptdjKOkYrhcFOlsGlFDB7ihLw069NcQYsgARV\nxbwPmg==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpUeE1RUSBxWFQ1\ndjVWdklaWGcrMUNGMmhlZHB2UHo5QkRQZUwvY211cEY2dVkrM1FzCmpjbnlpcy80\nUE51b25uRzRMZWJQVG9sNFN2b3Rld1hGU05zOVRrNi90ZUkKLS0tIHlGQkRkWWlQ\nZmxJaXpMWkVHWHFCckxRSzd5TEpVMG5za2ExWGxDU29VQjAKTvvPKG/elqWJa/Bk\nLxhBcCqXi1vVAEGcjBQ5kmoqswhM+Y8+WFOs+Pmiy1HFXjqmkK/ME1MppsT/YaBZ\na0ZJEA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA+M/DHDlKgayM6wsiX6r704pE+2qENOsKcytC7sBhKA",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDF2bHNJdyBESVd5\nVEVuQjZ3aklWYUdQdTNpWThkVjgyaDlVSjhjQUwxa0Q1T241WjFnCmRvZzZKYS9T\naTI5VVNVU3ZaaUN4MGNVc3ZLOXV1RVl3OGJyRDQ5QnZUN0UKLS0tIEhMaStzakRO\nN2VtVmZYZ3QxZFN1NTZKd3JFSXRrdnUvRkRMTkpLNHFjd2cKvbnwL1knid54Uree\nK5a/Yl84aJLzSpxPcg+Ak7qPOhtJ48k1OBUE2sGuoC4MA5FIGTpBIE2DookU9UR0\nUM1CXw==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDF2bHNJdyAyNFp6\nQktiNEZaOWVZbi9uSjZQQmJ2bG5FOEUydDY4cU9yVFJnbFpsRVZnCjNJckY0UWw5\nZTVRSFF5UkZ6SW5Wb2ZvZkJvamFTUTNPV3FmcHUxcHNOUEUKLS0tIFdRcGQzM25L\nZmdqMjlVY0w2clVFcFVHbUNiLzhlc3ZScEN0dlZlTWZkSUEKvVl8zp2yUIgxstsW\npoYkA+JwLcW0NBUm7X4ztBn88WVRnJU4cql2jPDHPl4ofclo4rdrpp5FNEbtTV/v\nBWhvBQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINh0gA7reCRW+zQ5pPpIjoJGpaFQSbC/4K8B6vMXJVr+",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHJKM2lMQSAvNmln\ndHBaYVcwTEdvNnFXWDFsNnJvZDFHUEF3RDZWSXJISitJdTE2VTBJCmd2ODVPQ3M1\nSXhaRysxYkc5dUNvRW0zc1ppeXVZbnNFUFhrcGduT0ZOdkkKLS0tIGVZMVR4ZTl3\nZWhYb3BOckRObUdXTmtrakNUbEl5cDh4SThZei9KQWppYVUKwO/B/munxTKaixao\ni3Gbs9l3XlZl8U2w8ul0PO8A/L0bFQ2xC+JXXdyu6OcAQk0aJbXwT7zRYQVuENn1\nLZKL8g==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHJKM2lMQSBhOTdW\nZFl5dGxZRjBkS244N2dqZkxKMjBuOFl0UjI5WlVycGhOam4zM1R3CnNuN2hIS2pj\nSmNtS1J0T3VZTkFON1lFdXEzRnJqV2x5a1ZtTXJQRWo5YjAKLS0tIDZYVzhtL2Yv\nYWwxc0xPMDB5aHI0NWdtZ1VEd296S2g3eXgrRk9FVW02YzQKPceWYgvW9cC1wHW3\nAaYjvd6M9lWFCD4cY2jpZ4M93vi1/mvrrYQHiZEGeKAeeEHM5wNknZHxf/nXAGp8\nqTVHbg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEmYihgWHfnLw0f8uTfLokCwToLavzV/+k/GggBA4Sz/",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGtNZGJ1ZyB3RFlh\nRE9jZDZkVUVpbFdLTEFzQkhGODFqc1g1cERvYXV4bHVNd3UrRHhRCmM1U1lDZ3VB\ndERIN2YzcWV2TXRqSEt0UWRKVW9yNktoZDhiQnVVeDJqeUkKLS0tIFNLTFVIYlpV\nM21FZ2RsM2g3Rm40dmp2YjJoS3VDd2VzOWJBRXU3THdORzgKDDqXM8vnJZfpGH8s\nTQqNP4PHUgnTq4qtRiH6gLJ8dTFrMEa9VCQsFFeCulJe5EKZEuHm6Ug9KBMN6D0Q\nOHQE9Q==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGtNZGJ1ZyAvM0Nv\nR3JDOHBBbktWMVI0V0JwejdtcG9RelE3RFRPUFRpUkVLTlhBdTNzCmVUYktPVWNZ\nRVVaMmxGQitDSHRsN09wSWtWem1kMld1d2FCOFM3ZVNlVWMKLS0tIHIwemRsVjRM\nZ3IvamF4ZmU0ck9wWWIySGZCc080S1d5QWs0dTB4Uy81M00K2shCoEwwtmsd1a7V\nn+bivytlJQGw8fmuZ2BmwCOaHVBX7ty1Pb6uUDWF9gvo7TGJSHaYUBVDfTC9dua5\nqakAEA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFvT1RvcTidgpVYE0OPknc3f5HcAVpyk+rXXrW4AcbzG",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFFONEwvUSBJT3g4\nZW16NWNkMTQ0NTh4SU1VV29QRkdNQVM1dVNLNUhSemZMOUljTWpVCmZnRjE3K3VC\nckNkbXBNdmlIVDRoRmxYb1FNdVBjTEs1OUw4Y09tQjEwbzAKLS0tIHFmOTZ0ZEUr\neWxaandjaU9kcXg0MUhUZU1NdnJ2TzNsTmxmSklhVUN4UTQKbJrQClJZd4KXqH/A\nHgxYtNWglBgzUGGYRkASvSk7ntA9Hn95lDGSYVoxPIV/4V8dYz3ZvLxhRmXD144d\nLdiNTA==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFFONEwvUSB2ZUNi\nejlQRXBxdElhV200VDhIcldnU0xqMXdaczhpR01HckdGWkduajFBCjdEOCsrdGtO\nMXFNaVNEOE56Rk9Bb0drbFk5MDBBUXdhRkxsblJaaUxBQnMKLS0tIEtwVVN5UHZo\nN3F4QnF2MnFVUUFMMCtRelFrKzFQcXNmNVcvYTJ1T1NSU28KXjeFW9kyO9tk4knR\nzwzd7lJ5ZK+vUyHjZaf367uk/kRwxhC8qTeM2BjosN5WYRGtDrJON+zgJob7tLKP\niiuRwQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEijeq50HS1g27BTRfJ8XWIPrAX9UVkap5fgIOCOtA5+",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDhjWXRrdyBvT2l2\nL3BBa1lPU09BK092TWU3WUFZTXNQcnJxN2tOQlp2cUpzU284L2hRCjVzdncrTnht\nSEQyMTFmWWcycC9QTGREK2FjR2JxUmxkWXBSSGJjZStPV2sKLS0tIEVSM2l1ZUFW\ncGhFVStvbWdQY2JZdmk3UDU2TkhCZENSTEJrWmNpblhVa1UK1MSivGg6EAB3QPf1\n2DfKfxy+MOVzai3zcXylBbMzxsc0gUYHt5+Q7iz7UYlegsXFYXoNyhObzdQqa3dE\nYANzag==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDhjWXRrdyBubHBU\nV0FkbGs0WUw0ODJQOTJKSlNmenBMSGE1MVo5MkxNSWM0dFlRS21zCm8wTXo2M0xs\nSTZBYnJURkJxYVp4dUIwL0xva2VIeU8zWGRQeWh3bmVMNlEKLS0tIENjSDZLb1V3\nVkJIbzVHUThwTkxLRmZIRWVYbGpNWUdPQ1pBTmJzcUI1eTAK8mTRi/HXqa7m9JPP\n2eS707bArqZYadGkscNEueHJVW8+FIIYD05WnrP6f3NO1hORoBrVTiFSRBRqEGkQ\n7Pc6EQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1d9h9mm3u5qalmpl2pf62pyzqj8t654n435emn93rutv0cg9sr32sg64fdj",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5Rk9jZ0lSSWE2LzhrVHl0\nNVNDVGVRTnN2U3YrcG1LQUR2TUF0RWtiQ0dVCmU4cUZKd1h0UUpxSW82RHFtbmdJ\nVlg2Z2VTTjk2N3h0cmt4NEg5UVhFYm8KLS0tIFB2a1p0OGM3cVlqQVBhb0hQNGxu\nYU1vdWJXblNnQmtvelJqNHVTVjdvdVkKsvlfK96k+PVg0KD/dv0LOGz7p8L/o0/X\nirO57zPYZBUVOEl2OyovRRhBzP8v21QIpF2A0UoZCYW5kaAMNy/a3w==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1Y2tiL2F0Wktkd05BVnh5\naDFoRjZyRmJFbkRraEthNDFwV2NmNC9kWEhnCldjYStBMWQ4S1hFZEN1dTRzbUpB\nTWVYZG1BbG5EM1Z0em9CUXRqczdjZnMKLS0tIFowNXJKa3p0Zng5bzQ1Uk5rN1dF\neCtGMnNaSXUwQlhRNDkzL29QenRSbmcKOVO4MzB3tJHUASuFX+33XKNHsH2DlJas\n8s6zn5pEuGr26fxH+1Jedzhzp+Y49ABu37SSiZwOpBBaLCEzOakAcQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuzbgoXZccZ2w/9HGgUyT9nJH7lG2/jfQCZJudY4yAN",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG05VmtkdyBSMGtj\nTklWb3FlTU5ITmgrVFowM3JtN3J1ZTk4ODAzM0dicDV2WjcyY2ljCkhhY21tWGRI\nRXhqMXZOWnhTbENzK1VlOEM4WGRUYkoxZzNheEJhblc2RXMKLS0tIDBIaCtWZ1Zn\na0N3SFExVHMyQytya3BuamE2cWd4TFZRemRxd2JjMytSc28KODYvGVb1DCTayFoy\n8lGVCvMozYys/ICgjrTH5IVAc6nsCVSXRQRKFlGCK4wXdhkyxaCVnXfWCGl50EhQ\n3zgq3g==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG05VmtkdyBqajk5\nV3E5YlRudEV2QndwS3dlclhmcXA1K3hmUUV1WG9iWmdJMnluM1NnCjBtcXhJU1lQ\nYnpqa2N2OGYvLy82Vkc2bnZqV2tQeFpFZDF5UEhqaDJWc0UKLS0tIG0xbVVEYkRq\nNStQV0NKbkFzMzFZWVRhUU9lQmZDSGFSWktqNmVqaWJPQmMKC/ANlnyPQDd/Q5+8\nzZ8mwLhBuClRIthySJCcd1f6G142I3GgnVQXSWYJi7JMmzuwlhCoifCf0LuHlvHH\nKy//bw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age16rkvks3tljju3y6xu0l7luhjzx634et97g3xe58xf2dgfn2865rqkq6t8f",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSRFkrV1RaZzFqNDFURysz\nV1hDNE9hUVJYTVZNc2FESllBcDF0R1JTT3pNClhIc29ZMnBRMXQ4WS82TXR2eXY1\nK2d4RUxCNmRPMkR6VjE4MVJDbWF5dVEKLS0tIG0wa0tVOHhJNnZCYTZheGRsNjNx\nR1EyOElCbW83UmI4cnZ2ZDBNSDZmSTAKzhoa3XgPMmJkvk5Z6G/ED5omE8fqYDNN\nr4v0bAe1kz3sYrEjxiUzgiHAsC+24azXoelEEWVXJ4hFBO8KQc/4ZQ==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBySGlDQkdhNWpOR0QwQUV1\nQldnVFdjdGhoa2xFOVVMekMyUW1vekhGMGdvCm5QaEF4UFNMcGdKUHJFV0YwYy9w\nOUovUlc2UUgvRUNFZG4zMXFrUzlEdXMKLS0tIDVQcVdBZk96WXVRSjZhbzZ4ZGpU\nSkNFK2NQaVppdktRN0s1NkV4VlpVVlUKZS4xGa3lD2s5iV9Xir/UIv7R4rw8122p\n0zIwG6cRSYpCHP6eUJsJnxDFlUWZL4sBHh9UDoUQ4Bz0M8Vzap3ZbQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFStro05R+CPmjWMHWtzXUKfGll+OosoZtXAyPtngN7T",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDRTOW45QSBtSVBJ\nTjk1b2tiazFjakJuMFBrUEgzaU5pQlZ0ekhiTEhpclYzU2QyWEFBClh3R3F4b3cx\na1J6cWswYlNIZVdoQnNjZnBCdjlVNDM2OXViRnc3WXAzZWcKLS0tIC9mRG1ZbFA5\nd3YrTDRENERIVTlFT1dpeEd4Z0VNYzBUdnVabmJwOTNpTjQKsWG6f4qZo5THQAq+\n5gYc/Urzo7CEfJvo2uHNIr5aamMV8UFdPPiK6KKrH98gv8L9zXMfMciDoxMgIrzu\nN+dB4g==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDRTOW45QSBsSjUv\nNktkam1zaktuZ3BwbjdUb3k5WWsxVk1aN1Z0Tm45TmRlYk4xMFhnCjZZQ293ZTFy\nVVdzdkJqM0Q5cWZSOHpmdXoyQUJTZVFzZnJFMmRNbzhwQjAKLS0tIGZmZ2drZFlI\ndFV1YkxWSHVGUVRKays2RS9JRFYweldlVnkvU0VoRjlBQXMKDxyqFP1Y3TBcR3uV\nfKChCQZrdy3l2CeZtx9P74BbW8vaO8oRcfigAki8a3JplNmX+UmKJIcbw5nKeU3y\nMwkBtQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO5qner13Q0fm5NhdXMx2nkt5kxjC0/SVY2FXh01OiHN",
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVucGlodyBoQk1s\nRjBJWlZIREFQbXJyVjh6eWdBZlVWWXFyRm45YjJEY2IxNjlTMVZNCnZ6dnMxNkYy\nKzB4UGlhQWY2Qzd4enVEdG1pS3NDMG9FZS9FNkdYdDBaSGMKLS0tIGIvblU1T2h1\nUGx0dnZ0bTlsS21xY2dNVTNnQ3B6NUFuckhwSkFNK3grbTQKp9RWavllfPmzitez\nTWTtcC7nuxiOuoac9Os+Pvuzj3j0B4BxxLNAT7vSVtlgOMl5Mfa5BOvNkcgo6o/x\npJPesA==\n-----END AGE ENCRYPTED FILE-----\n"
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVucGlodyBqcWtJ\nbHNaV0lXczB6SiszNEl1Uk5PajBoRlhwWkUxVytsQitrZ2NKRGpZCnQ0YzNhTkwy\nc2tDWEZOT21VUENPcVU5alZBdGVrZWZUbzhtVm5oUDdoQWcKLS0tIFBCK0ZPMVdV\nVHBpNFBwM0N5TFRRcFVZbG9sT3pkWm05cm9OdjRqVER0enMKWXQYx35T61ncNCdc\nD69DfTW8TMX+fqeczScpd7II+Rai5FRa5l7mev/zNx4rLLU5DmfocYBuzcTEC6mj\nk8xfUw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
- "lastmodified": "2026-04-29T10:06:36Z",
- "mac": "ENC[AES256_GCM,data:kroqMERyYthC87raUeryIkgSrxTRvbx9/t4ZD4x27EKQEqpy6SzhIY1bOtoGvROrHf3gtrQ6ZKsQIVeXHRkrZCAchaM2Zf+NhOvwypbxsiy0RNnZtYNidNIH/ByELpUbCSVe9SqNPubFm/fWe0V2FdsmtpNW+kcrFAaey350+fM=,iv:EYphE4seDJuZk6ZW16qfn5W6sOu46ju1Udveqv3i5Tk=,tag:3xuRpDyAlfxOmLac2Di6CA==,type:str]",
+ "lastmodified": "2026-04-29T11:18:55Z",
+ "mac": "ENC[AES256_GCM,data:4WqU/2SV/QoQ3sN9MWhdDJ573qe7KmyB5TNY78laXCQ4p4HIhcYTEqDl9BD556OkECwSawtP7ZES9js0ywU3YNmVd9axyGWIdiR41OSXjCagnfcFg6KJf6njQePpV35hR/ZyzXNrSDnIijo1QOgGUTcifG9yI2UIlJfCQockE+k=,iv:ifzf2V1978sZB2kdlWB9INXkGhRb8oDOPjIaHD+uYPo=,tag:3O0ja7T78q+H2K4X3OY79g==,type:str]",
"unencrypted_comment_regex": ".*",
"version": "3.11.0"
}
diff --git a/packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts b/packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts
index 789f00e0..3b72d05d 100644
--- a/packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts
+++ b/packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts
@@ -1,92 +1,92 @@
// Auto-generated by Stackpanel — do not edit manually.
-// content-hash: 09041ab4ece80e846e9a581e401729b77d20f42ade25d821d76802fed8489971
+// content-hash: 4d403c0d41a32e1454031b9ae70b3c18020c7b33f843969ada1bc40a873c80f9
const encryptedPayload = {
- "ALCHEMY_STATE_TOKEN": "ENC[AES256_GCM,data:rTBiBapZCBJFCuXzp8f6VezyB3mNfwONjBU=,iv:qZWr5xm3G3pUMkWGQ7SX4jeui6qxIR3aqecUTbBvTEo=,tag:GBXJiGBpq5/Hx5MkZIO0Uw==,type:str]",
- "AWS_SANDBOX_ACCESS_KEY_ID": "ENC[AES256_GCM,data:FtAfQI0e9lubjlhRiuZDLkkzdYA=,iv:GHg/HPyKqWxQVnHB7+CljxO5wdMWNEQhSU/6gsUPyZE=,tag:AiXojokdTW2sqs2BP7VWzA==,type:str]",
- "AWS_SANDBOX_SECRET_ACCESS_KEY": "ENC[AES256_GCM,data:KS5MH2XWo2cJ8luY9fgiuy2ws8+yPvJnz7PHthjllfQbMbFPji8rZg==,iv:cNlBBQ1n1WI11TBfMGGL3iRcj9OczeFWyeNwdmJgvrk=,tag:rG4mmfJBHjyVXQ7H5vn6eg==,type:str]",
- "BETTER_AUTH_SECRET": "ENC[AES256_GCM,data:Ak2jq5yT+bwfJ5Y5xC9ehRZimpvRKu8yG+KowhKY/t+wqrErA6XNFSPjeQY=,iv:6BAT/PGqkNDmOJxvgNm3FcZOHLMw8Dyk8wpZCUQIAO0=,tag:ihaG3P7ANyKYBZTG4asNoQ==,type:str]",
- "CLOUDFLARE_ACCOUNT_ID": "ENC[AES256_GCM,data:OFw5coSME91vXXYBX6ffTEDtw0K05M/An+NDl3MEF4k=,iv:gQOIkTkFOUUDlQ5bZExGMdfUkLXpdqsLW3JM89HHohw=,tag:iYCJ3yHiGp7FozCQoUknRg==,type:str]",
- "CLOUDFLARE_API_TOKEN": "ENC[AES256_GCM,data:tHZuQsE380/tDNwfVUO1h1Loo2BuueSuCsr84OkZ6ZvNg3PO22cfDl6wIWu/JWIZM6WLehU=,iv:Go3pv2JOk8kYtSLyYpWWco3bKaFCECEPXQ4clAAB5ak=,tag:KQvOu7gISqs881hgLc8G1Q==,type:str]",
- "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_ID": "ENC[AES256_GCM,data:IU4XL8j3GbYk2DiBw+P7B7VYdP8p2UGKoBItAR5L1XSgzEfPKCCa,iv:4gutigTJknDBUvoDmvMvOWs1GHXLDhVeJGyAE/37Gcg=,tag:tBb9GuOftO12pDP6kVf6gw==,type:str]",
- "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_SECRET": "ENC[AES256_GCM,data:AsArdfpbL0O/Ojkt0Q2i9GuJ+hKotFNhoVr2RMGDrbcG3WKBFdr/zhMUVULlZlakvmiK3+MDMTpGxol01lr3UA==,iv:9FFKl+bAu2C+71zlRbuVJKA5LMq9PXUdGsrbWIsP++0=,tag:/7wbmQqyYNyODgmMW87JWQ==,type:str]",
- "HETZNER_API_KEY": "ENC[AES256_GCM,data:dG36TPkWVtb6qVa7BIE3UF/fKJnviSWGy/WzKHSRVSmLdtOhp3LYuDs34abNtT/1ZQRduE77sAYbDlNYLx/rLQ==,iv:TBpn0Npv19x+2F69BFWZUPsahT5BzSFHN03o9U8iYEI=,tag:BWN6qOFScqSu0Okcik4CuA==,type:str]",
- "NEON_API_KEY": "ENC[AES256_GCM,data:HolPRRDTnvsXbwXPF09rNXVJhw9UQF1s26IS0WolWGo5scVg6LA2LlIQqQlI+AZSuEClZ9axP4qhSJigrqxiwhKhOdWv,iv:1Ye1rSMUyVZRzUca/k0Kh0zYRJGJxubj8JHmfkwPUHE=,tag:Fl6PzPUBZ7pPFxDf/FB6Lw==,type:str]",
- "POLAR_ACCESS_TOKEN": "ENC[AES256_GCM,data:8B1tMabKQBBf9pVPv5dm+Vh100dHui2JqJsPiIoU9Bno9ikS7jU=,iv:rA13nuouvQ+X8oIi2wKv+gebP/6OGk/frtqtbh48oN0=,tag:Leu4q/yuI7SIdqIzaYIvfg==,type:str]",
- "POLAR_FREE_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:vBnCCRo+OKLbWbhsSmW6gisSI5AvsAdY,iv:Kx0zkQK1IZS6aQoDRJ1lSZM/DUZbkXSquhH1HGPPK1g=,tag:fc+wD/KV54d9AFmOKVTOkQ==,type:str]",
- "POLAR_PRO_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:LMKgcw214sz/gXo2T6h00h4S7u2Aiak=,iv:J+RunAALWPeK6j8z8RTDoX+rckalVE4AFltWmuD0km4=,tag:T4nc5+nKC+wEsV17dld3mw==,type:str]",
- "POLAR_WEBHOOK_SECRET": "ENC[AES256_GCM,data:VLXmN4UWFjJjVDeh2nA5OpDyuAiBBqFK4GaFmOHgpKl/Ww==,iv:17PUD90QslYrtx2H4Y9vNrlHgiw3PjQz/G9uhpVeRPc=,tag:8e4uZdupkWm2xSTXuFHX1Q==,type:str]",
- "POSTGRES_URL": "ENC[AES256_GCM,data:THuclyynG8PyTlVVQM0aaCRCgfGcNRCV8OJbZuhLhMswuFXz9KJakGiVCa/tfGXelpTcoTB+edIS/g8k3GqOTBt3ii67KUQEK5XX0ChW++IcR6g3y8m1cq6aiGqEx9EXYZyv2VqEv5dJFN7JonQtbiobpkpU722JyHwdcOzPOfqTio+3HBzVKw+BOQMRMPAH0haPj/ggeiVojQMra3PyeenMAwk+BJngzg==,iv:M3M+/f76EW4yux9DGZv+RmUV4uvXQTMbfD0kMbB2nEY=,tag:C5aQgNgGzmfiHl/Iq13Xdw==,type:str]",
- "STACKPANEL_API_URL": "ENC[AES256_GCM,data:TzF0BYY0DX78f7UllHGj4iaTzE/MSLV15wo=,iv:0uDDBFCigDYkPPCXPOF8gVz7GkD7u3ECzHlU52rt72c=,tag:Izgidk9pFMPLCMAQgm1pEA==,type:str]",
- "STACKPANEL_STATE_BACKEND": "ENC[AES256_GCM,data:03Q3pxs=,iv:MBCDQ2yBJUM/ytwAoTmEt+Q5KB2dKcH5UQ5g9AvxCYE=,tag:os/gN98UCeJiweIGtbIfEg==,type:str]",
+ "ALCHEMY_STATE_TOKEN": "ENC[AES256_GCM,data:GBcVz2EiFOvIIih333Lw06ixU/rpMfBO210=,iv:7U9U0ZT3csEhINsSold/cnTCDXnmbxFVYJXvyPDHOnM=,tag:Cn64WbbWWtXPk8f1kLot4A==,type:str]",
+ "AWS_SANDBOX_ACCESS_KEY_ID": "ENC[AES256_GCM,data:Jx8DQgBTwMqj7pTj25oNRRw+yvk=,iv:YXmcb/rf8LmP3S++emsugIn6Ruxcy0uYqLaGlg/D13k=,tag:mbjfPPcFwkUwZCrY68757w==,type:str]",
+ "AWS_SANDBOX_SECRET_ACCESS_KEY": "ENC[AES256_GCM,data:odETLJwgoP4ygcxl53+p4syxfOo6jjTf9tXmF4Fi5G77+W+FvUhOjQ==,iv:vfUPoM585zkANi2EJaO1p8sB5tb0VfdSdNXyZr7Hevk=,tag:qqUkgmkFJw9JoYdASABVpg==,type:str]",
+ "BETTER_AUTH_SECRET": "ENC[AES256_GCM,data:CGzPpiRyq2bQddUcuULEqAE7HVd7ae8pkpEBK9S4B7h6UalHfP16gftvaxM=,iv:T4WpjIdpfKkRyTc60wDKBvqMezGSsBRejV19X5zIxis=,tag:48iKQt4JKp+svpTW91SU3A==,type:str]",
+ "CLOUDFLARE_ACCOUNT_ID": "ENC[AES256_GCM,data:qUHUaEAT3kCYWXHWJDvLNFWe4hxGH9i3J27X/0oCxto=,iv:yFO2whpGJHTXu4SX+IBDC+QF0/FyPvZKfVJZY/PyMAU=,tag:+HbNzS+IgkWuK2m0LAWx3g==,type:str]",
+ "CLOUDFLARE_API_TOKEN": "ENC[AES256_GCM,data:kwuguFkv40mnCnnbtr6sqaF0tH0gg4xrFDjn5LSc2rpnebAschpu6xA0rs0EX+i0ieN6UZs=,iv:yJVrAal8GkhKOqMVYR+r6IGtH7tElsf3Oue9JSQIDBQ=,tag:C8GfQFNsWk6b+RgWQClTQA==,type:str]",
+ "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_ID": "ENC[AES256_GCM,data:g3zfDSaX4cw9KI6J4YJbObN+BLkEbYeSrmkqaaU1bnInC2/w61UG,iv:mdZdgGY6JFFK2R3k+5MTPN394Rdwl4qWD6P7bvqMRLY=,tag:+5qQn4LQq3RJlf0iNIwFJg==,type:str]",
+ "CLOUDFLARE_SERVICE_ACCOUNT_CLIENT_SECRET": "ENC[AES256_GCM,data:NEZPMzorh2YchykdkZ4zDGDKcmishp1XphdDLrr39Ca00H04DKCmUNIr8RLBS2QQSmb4pVrdU2kAGnr+on6xvg==,iv:sSp3COVynQkSK/VNje1O5wehuhfTQRYKdknVey0UGqM=,tag:bBuvfxkXT+2ysjogC8iU1A==,type:str]",
+ "HETZNER_API_KEY": "ENC[AES256_GCM,data:j2ofslGsxLUki6b08NxpGZjcInBUlLd3U8IL8FyOP92BGBpdMGa0bky9V3kQeHdyXxX83B2a4+dyg+SFZnlJMQ==,iv:+G1vlPOCQ5fVMoaluSkpOaHClcUWzNbawsZyE5lx1h8=,tag:oXnyowIVoSG8Fgswod1AkA==,type:str]",
+ "NEON_API_KEY": "ENC[AES256_GCM,data:UtdrbQjpxO569cnvx1YpFJbrpbd/KnYB8caj1iAReABKpJthTzSux2zd9Z9RN4Inp2zmhLWjRQ5WejWfYZDClzNTWRf1,iv:lijmT6sZI3R5++NDVPctRF5TGdBbncxA8+byDGSccKo=,tag:9Y3+QL1kc1CeRhmvacRfUA==,type:str]",
+ "POLAR_ACCESS_TOKEN": "ENC[AES256_GCM,data:3onI0xX7R1kCR5u2IaESrmGfhFGo5R2kKT/b6qDTnsBHSsqZItQ=,iv:pZi5xTotR0Ho9y4e94Q3XXUnuGUe+/2wt7yz1JcqExI=,tag:l46niw1WC/07x3BbkNwVkA==,type:str]",
+ "POLAR_FREE_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:6w7FBOGFK0o9FBRlioZTZptElWYtZ6g8,iv:9XNuy0MTEn1upFWqbvpLeeBOgIpK1Lhu9nZpxu3ZMtA=,tag:mgMxm8iaSvepafA9H6FaTA==,type:str]",
+ "POLAR_PRO_PRODUCT_ID_PRODUCTION": "ENC[AES256_GCM,data:G9VpESvGAXaC/aYtERIOdoNHMpbVkzw=,iv:dN/yu71QOrKekJ2upYK72SBAjr+ui/JMZaZOLnjBgGU=,tag:hqcfP9z2satrqOq0K6giCA==,type:str]",
+ "POLAR_WEBHOOK_SECRET": "ENC[AES256_GCM,data:d5fYyac8z3t85qHL7Y0eDyggBXTABrLmpnqX0GOFry7JRA==,iv:betPLe/SFjoQPW41ffCFeGUXoKlY9K6zDVSU2hjmD04=,tag:cXYfpje4yppB7VvrNHb4gw==,type:str]",
+ "POSTGRES_URL": "ENC[AES256_GCM,data:35yZq2/My43JOB79D8IY2YOMV+ICkF/d8dYVg3ig193tCUWW8tdL+8/KOX5RTD1vmcRW0acXfN7inGx1SaOKAZnbU0tizHYXAHziDfrMmRnIq5T++Lt5ZZ0FdiuzSPAvpYsvPPaaI5mH7L9tzK4iovfXMC8bn6ktKYPmoZByiZ99jAWj3qPQxUh4KLMxaAi+O5gX8em2jsY06ErVVldoqzs2eyKCUNJG5Q==,iv:re0opsnqllbKCCoYfzyZuMmPi7YY6cJHKS7MS6V8OMc=,tag:wJgzC+GblqQnf29pnYQLaA==,type:str]",
+ "STACKPANEL_API_URL": "ENC[AES256_GCM,data:cZs6KnGRIKTxAZ48pDahAyJu8OUYUVHjPn0=,iv:qEPiZt17oIqE+XKe97QdlJlpxXwcb4x6m6811qYCr1U=,tag:29HIaBBYVcD5GtJ2v2TgMw==,type:str]",
+ "STACKPANEL_STATE_BACKEND": "ENC[AES256_GCM,data:W5giOY0=,iv:SM6ubBsxMcSNFRr1kaSif9ktsKYDVqo8BtaQVxBRryU=,tag:iGUVhdiRWiR86drWokVJMA==,type:str]",
"sops": {
"age": [
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHZMYWFYZyB3RFpi\nNnR0MndOdVlaZDdXT2oyZ3U4YjR6SXFhN01RLzJtQnZtZFJSTG4wClAvTFJYNjlp\nVy9ITlRmWkhGTEhRc2tTT3ZiN0FrRU9SQmNVZDFxOGNqd00KLS0tIGVzSkphdEZa\nenc0L0Vpa1p5VXRUOXRVV3pMUUNRT0k1L0dLL2l0SHoxeEkKBrzAJ6XZD8PEE2Ut\nWi8TBrllT1qYs+wtbNA8T9iZF1ifndkjRdxxZ0EkWsdypOw297XIfKPQsqajIe5c\nmwdnlA==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHZMYWFYZyB3Yk9t\nUUlhZWRhVitScklxMjhRbGswRy94L04wWDA1bG80NXZPVVFDL2lVCkZGVHJIN1Vh\nNUpXN21mU09OWGNUYVJYREw5azJ5NnpyYllDK1FxTXRFVTQKLS0tICs0dEFWcU43\nbkN2ODd1U3NDT1EyTElWNFZqLzhVQTN2ejlOZURDZENNQ1EKG5Xb/q1q4uZn5VcE\nMRHUMyIgL4WLHdsH/JjecTyeMB9foM7njshWx3eKh+Ln4/nyoUle/wrVzkHucggG\nup9lRQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICjMrEzKQSq/0eePzgUGXM349sBm6GVCXoz+tJZJaBVT"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEtlVWxBdyBFK1lV\nNTdlMU1NenpmMkZUY2dmbnpKUkpHSkRJRHZ1MDV6NTJTb00xMFQwCmdGcW9TS1NQ\nN3A1b3kwNXhUMHd6VzRJMjI0NHArdXJ5ck1vQmE0S0pDL1EKLS0tIEk4TGJ4ZTNF\ndGR3bDhCNzJNMjVZbW5IUlZnT0ZPcWd3UlR0NVFCQkdHKzQK+nefxkP3XX8keweE\ntUB85kQv9AYnIP7LDv16cBa17RCtOSaDI1cRjEGw3K+skLffOKxR4d8l/715+2ou\nHMfpGQ==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEtlVWxBdyBnU1d2\nRFZRaXBmUVJvaWFENFRVMlJ4WWV3MVJzemg2bkM3amFFUHp5Z1ZFCm91T1ZUNmVl\ncmJZVCtHazlsRzBVRFpzYisySGRjTUZKbkdrU1FPWHFEMG8KLS0tIE4xWEpPSmlB\nTG10MDNYNFNHbkxLbUFacDQrYmI0OStTKzViSDFnMnh2UkEKD8NQFopgJycqwRTQ\nPDZVo7uMG2wJYW+F76puwSlgWN1udYOTzgrYwPlvIOcJs53Coz53jCNlGys2APQK\n4sSWDw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIoX0wKjybps6ZHuvA+uwE1ThfWl87MApDLxCjCDUCC9"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGcrY0ZCUSA3NTFo\nZE11YithNitEZXRRWDdNekFXcFhzN2YrZlFSRTN4eGZsTEpITXc4CjR1dzVwQmVC\nV2ZkM0h0Yk1JMHJsVDhhanYwT2JvSTZnTE55YTh5SzVHYWsKLS0tIDRybGdXeXhZ\nT1BEQzVqVHJPcCsycmJ3c0FISmpBU0QyL3VBcUR5STdoNkkKp/CDw996X/FDhTIL\nmeMdVN5kL41zaRHTrSpmN8bhUG/0eL7QP9vurTpz24P6Dk1qvGBjjhhcqvRW9WIf\n5yVsCw==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGcrY0ZCUSByZ3ht\ncW1kRzk4WVdGbThsTzdyampFUTlVVmZFWXRTUHg0cjFXUXJtc0JVCnlZYlZYUzc0\nSHlSejRaOFpOaDRlRTlqc3JtR242RFgva2JiUnowWCs2aDgKLS0tIHlXZnNVcFRD\nMEI5aE5oNkZ4OGZsb2ZmRDFaOTBac2VnS29ralYrNmZ6U28KWuoTRneXf4VCsO4e\n7la4d4ZkuYx7DeBWoG14+oZzgcWGrBXWhGIyc6DZL/z4mwQ0xhZ2qkKIWLih5BsG\nwhTznA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGbPiUycu2bZdvvd+b4b2OtjSMMcIhH/lmedaMnye0BM"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDFNdXZydyBYR1A3\ndkFLZ1N1emovV2g0c0VadXZFL0dxaXlKUHk4V1ZPQzBPOXVnWndJCmpXc0J1TmZD\na3l4SmhJWmZEUjUwRGlxNTI5RDVUcjRwTWllZlNXRVQxZ2cKLS0tIFJLb0g1K2FJ\na2xXRzV1WnozMjd1WGU1UmVmWW9RdmVhOFpqdUxvN05HakEKhiisF8PSRNG7klo1\npj5gjVX04D2n7AEzNaTO9ccvz55d/NYN7AJUy2/HIpVc+3am6FY0oCzmP/jwsYd2\n1K2FuA==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDFNdXZydyBybUVm\neHV1QUV1OVo1UEdJRW9YYjNOejA1cnd0RHF0eTlEdVhRMXBoZEJ3Ckd2NnFSQ0lF\nZTMyMmtWbkt3Zm81NVl2Q1p4dWl2cEJyUlZsU3ppRWFSZnMKLS0tIFludFZhKzY1\nVnBwSzFPaVVWR1gzWjNySmFhZ3lWek9FU3czMnRxL0cybWsKgtBwibqNpljgZtMy\n7tvND6kgQiSCoGe0kvMSfDcRaIDvh/oCjbu5/vr7s40oXZKKo+b6Uzjbfx02qh5F\n0GkxUg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKZPhznHjTDifGroHiGRD3hwWz69B8NsSSPNbwWjfzgW"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGw3N2NtUSBqZlJi\nb3dvZjR3ekVPVzl1R09XbWRQUkNURWJqN3lQdURHWUsvUS9sR2tBCndtUkd6azRu\nWC9sTFJOb3JwR0RLMkVYdU50SDgyNGRoZjFtNzFNMUdnY0UKLS0tIHVWbVluQTBB\nSmtGeTFjMW0vWGIrbVZlQlM3alBBM0tRUTBabWJISlRnOU0KWcDcyXU9K0wxKIzr\njQtFSSPQDsgd0Tur/cBK41ikxn6/BcdVBCgE6zsllTvP79sm/sTdslq5q2OZGUJv\nbwg0Eg==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGw3N2NtUSA2cnhP\nNFNWQ29KczhteVBkbytuU1hXdzhkdjVTNXAzay84NFF5dXNDakdjCkRxUlhDdHIr\ncDFnL2NGQkxGWnk2bENCa2pqQXVXNFl6cVBsbGxNNncxMkkKLS0tIFd0d0NjdmhY\nUHZMZEZqT3RDTndXN1FkdU95MXZRbHFiSU1RWVY5OFdYc0kKmH2MSGIb4KrEkySo\nuXngFJs4G0qUOZ20pJlOlI8o/WD6si3huRDXJC720p0ePldlsASwE1nidX3oTjBf\ncO/8Bg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJF9kzudl00FVTQA7nnSaicAJW2MoBKm47G7wefb6uSm"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpUeE1RUSBncGVD\nNHlZY0FuTjJhWTNTcS9xdlY0bEtyNjFyWXQ3Z3RadEtHdFNaanhJClhldmQ2a2h0\nRkZXTkNhZnhScjgweWZ1bndmNFpBbmZ0WnAyYmMxbkdSNDgKLS0tIHdpOHFWb0sr\naitlVnNUNENacjBlZmt5dWtHK05iRXFjeWthNHRVL0t1ZzQKGpBxMEoqm44l9E4U\nOzEMFrwh/r4I4XDE7x3G4dlZVEuptdjKOkYrhcFOlsGlFDB7ihLw069NcQYsgARV\nxbwPmg==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpUeE1RUSBxWFQ1\ndjVWdklaWGcrMUNGMmhlZHB2UHo5QkRQZUwvY211cEY2dVkrM1FzCmpjbnlpcy80\nUE51b25uRzRMZWJQVG9sNFN2b3Rld1hGU05zOVRrNi90ZUkKLS0tIHlGQkRkWWlQ\nZmxJaXpMWkVHWHFCckxRSzd5TEpVMG5za2ExWGxDU29VQjAKTvvPKG/elqWJa/Bk\nLxhBcCqXi1vVAEGcjBQ5kmoqswhM+Y8+WFOs+Pmiy1HFXjqmkK/ME1MppsT/YaBZ\na0ZJEA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDmZV9Oi9hrckXTpqBnDKRrY9mgwj+SJeVBGWmne2+q5"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDF2bHNJdyBESVd5\nVEVuQjZ3aklWYUdQdTNpWThkVjgyaDlVSjhjQUwxa0Q1T241WjFnCmRvZzZKYS9T\naTI5VVNVU3ZaaUN4MGNVc3ZLOXV1RVl3OGJyRDQ5QnZUN0UKLS0tIEhMaStzakRO\nN2VtVmZYZ3QxZFN1NTZKd3JFSXRrdnUvRkRMTkpLNHFjd2cKvbnwL1knid54Uree\nK5a/Yl84aJLzSpxPcg+Ak7qPOhtJ48k1OBUE2sGuoC4MA5FIGTpBIE2DookU9UR0\nUM1CXw==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDF2bHNJdyAyNFp6\nQktiNEZaOWVZbi9uSjZQQmJ2bG5FOEUydDY4cU9yVFJnbFpsRVZnCjNJckY0UWw5\nZTVRSFF5UkZ6SW5Wb2ZvZkJvamFTUTNPV3FmcHUxcHNOUEUKLS0tIFdRcGQzM25L\nZmdqMjlVY0w2clVFcFVHbUNiLzhlc3ZScEN0dlZlTWZkSUEKvVl8zp2yUIgxstsW\npoYkA+JwLcW0NBUm7X4ztBn88WVRnJU4cql2jPDHPl4ofclo4rdrpp5FNEbtTV/v\nBWhvBQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA+M/DHDlKgayM6wsiX6r704pE+2qENOsKcytC7sBhKA"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHJKM2lMQSAvNmln\ndHBaYVcwTEdvNnFXWDFsNnJvZDFHUEF3RDZWSXJISitJdTE2VTBJCmd2ODVPQ3M1\nSXhaRysxYkc5dUNvRW0zc1ppeXVZbnNFUFhrcGduT0ZOdkkKLS0tIGVZMVR4ZTl3\nZWhYb3BOckRObUdXTmtrakNUbEl5cDh4SThZei9KQWppYVUKwO/B/munxTKaixao\ni3Gbs9l3XlZl8U2w8ul0PO8A/L0bFQ2xC+JXXdyu6OcAQk0aJbXwT7zRYQVuENn1\nLZKL8g==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHJKM2lMQSBhOTdW\nZFl5dGxZRjBkS244N2dqZkxKMjBuOFl0UjI5WlVycGhOam4zM1R3CnNuN2hIS2pj\nSmNtS1J0T3VZTkFON1lFdXEzRnJqV2x5a1ZtTXJQRWo5YjAKLS0tIDZYVzhtL2Yv\nYWwxc0xPMDB5aHI0NWdtZ1VEd296S2g3eXgrRk9FVW02YzQKPceWYgvW9cC1wHW3\nAaYjvd6M9lWFCD4cY2jpZ4M93vi1/mvrrYQHiZEGeKAeeEHM5wNknZHxf/nXAGp8\nqTVHbg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINh0gA7reCRW+zQ5pPpIjoJGpaFQSbC/4K8B6vMXJVr+"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGtNZGJ1ZyB3RFlh\nRE9jZDZkVUVpbFdLTEFzQkhGODFqc1g1cERvYXV4bHVNd3UrRHhRCmM1U1lDZ3VB\ndERIN2YzcWV2TXRqSEt0UWRKVW9yNktoZDhiQnVVeDJqeUkKLS0tIFNLTFVIYlpV\nM21FZ2RsM2g3Rm40dmp2YjJoS3VDd2VzOWJBRXU3THdORzgKDDqXM8vnJZfpGH8s\nTQqNP4PHUgnTq4qtRiH6gLJ8dTFrMEa9VCQsFFeCulJe5EKZEuHm6Ug9KBMN6D0Q\nOHQE9Q==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IGtNZGJ1ZyAvM0Nv\nR3JDOHBBbktWMVI0V0JwejdtcG9RelE3RFRPUFRpUkVLTlhBdTNzCmVUYktPVWNZ\nRVVaMmxGQitDSHRsN09wSWtWem1kMld1d2FCOFM3ZVNlVWMKLS0tIHIwemRsVjRM\nZ3IvamF4ZmU0ck9wWWIySGZCc080S1d5QWs0dTB4Uy81M00K2shCoEwwtmsd1a7V\nn+bivytlJQGw8fmuZ2BmwCOaHVBX7ty1Pb6uUDWF9gvo7TGJSHaYUBVDfTC9dua5\nqakAEA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEmYihgWHfnLw0f8uTfLokCwToLavzV/+k/GggBA4Sz/"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFFONEwvUSBJT3g4\nZW16NWNkMTQ0NTh4SU1VV29QRkdNQVM1dVNLNUhSemZMOUljTWpVCmZnRjE3K3VC\nckNkbXBNdmlIVDRoRmxYb1FNdVBjTEs1OUw4Y09tQjEwbzAKLS0tIHFmOTZ0ZEUr\neWxaandjaU9kcXg0MUhUZU1NdnJ2TzNsTmxmSklhVUN4UTQKbJrQClJZd4KXqH/A\nHgxYtNWglBgzUGGYRkASvSk7ntA9Hn95lDGSYVoxPIV/4V8dYz3ZvLxhRmXD144d\nLdiNTA==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFFONEwvUSB2ZUNi\nejlQRXBxdElhV200VDhIcldnU0xqMXdaczhpR01HckdGWkduajFBCjdEOCsrdGtO\nMXFNaVNEOE56Rk9Bb0drbFk5MDBBUXdhRkxsblJaaUxBQnMKLS0tIEtwVVN5UHZo\nN3F4QnF2MnFVUUFMMCtRelFrKzFQcXNmNVcvYTJ1T1NSU28KXjeFW9kyO9tk4knR\nzwzd7lJ5ZK+vUyHjZaf367uk/kRwxhC8qTeM2BjosN5WYRGtDrJON+zgJob7tLKP\niiuRwQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFvT1RvcTidgpVYE0OPknc3f5HcAVpyk+rXXrW4AcbzG"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDhjWXRrdyBvT2l2\nL3BBa1lPU09BK092TWU3WUFZTXNQcnJxN2tOQlp2cUpzU284L2hRCjVzdncrTnht\nSEQyMTFmWWcycC9QTGREK2FjR2JxUmxkWXBSSGJjZStPV2sKLS0tIEVSM2l1ZUFW\ncGhFVStvbWdQY2JZdmk3UDU2TkhCZENSTEJrWmNpblhVa1UK1MSivGg6EAB3QPf1\n2DfKfxy+MOVzai3zcXylBbMzxsc0gUYHt5+Q7iz7UYlegsXFYXoNyhObzdQqa3dE\nYANzag==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDhjWXRrdyBubHBU\nV0FkbGs0WUw0ODJQOTJKSlNmenBMSGE1MVo5MkxNSWM0dFlRS21zCm8wTXo2M0xs\nSTZBYnJURkJxYVp4dUIwL0xva2VIeU8zWGRQeWh3bmVMNlEKLS0tIENjSDZLb1V3\nVkJIbzVHUThwTkxLRmZIRWVYbGpNWUdPQ1pBTmJzcUI1eTAK8mTRi/HXqa7m9JPP\n2eS707bArqZYadGkscNEueHJVW8+FIIYD05WnrP6f3NO1hORoBrVTiFSRBRqEGkQ\n7Pc6EQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEijeq50HS1g27BTRfJ8XWIPrAX9UVkap5fgIOCOtA5+"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5Rk9jZ0lSSWE2LzhrVHl0\nNVNDVGVRTnN2U3YrcG1LQUR2TUF0RWtiQ0dVCmU4cUZKd1h0UUpxSW82RHFtbmdJ\nVlg2Z2VTTjk2N3h0cmt4NEg5UVhFYm8KLS0tIFB2a1p0OGM3cVlqQVBhb0hQNGxu\nYU1vdWJXblNnQmtvelJqNHVTVjdvdVkKsvlfK96k+PVg0KD/dv0LOGz7p8L/o0/X\nirO57zPYZBUVOEl2OyovRRhBzP8v21QIpF2A0UoZCYW5kaAMNy/a3w==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1Y2tiL2F0Wktkd05BVnh5\naDFoRjZyRmJFbkRraEthNDFwV2NmNC9kWEhnCldjYStBMWQ4S1hFZEN1dTRzbUpB\nTWVYZG1BbG5EM1Z0em9CUXRqczdjZnMKLS0tIFowNXJKa3p0Zng5bzQ1Uk5rN1dF\neCtGMnNaSXUwQlhRNDkzL29QenRSbmcKOVO4MzB3tJHUASuFX+33XKNHsH2DlJas\n8s6zn5pEuGr26fxH+1Jedzhzp+Y49ABu37SSiZwOpBBaLCEzOakAcQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1d9h9mm3u5qalmpl2pf62pyzqj8t654n435emn93rutv0cg9sr32sg64fdj"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG05VmtkdyBSMGtj\nTklWb3FlTU5ITmgrVFowM3JtN3J1ZTk4ODAzM0dicDV2WjcyY2ljCkhhY21tWGRI\nRXhqMXZOWnhTbENzK1VlOEM4WGRUYkoxZzNheEJhblc2RXMKLS0tIDBIaCtWZ1Zn\na0N3SFExVHMyQytya3BuamE2cWd4TFZRemRxd2JjMytSc28KODYvGVb1DCTayFoy\n8lGVCvMozYys/ICgjrTH5IVAc6nsCVSXRQRKFlGCK4wXdhkyxaCVnXfWCGl50EhQ\n3zgq3g==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IG05VmtkdyBqajk5\nV3E5YlRudEV2QndwS3dlclhmcXA1K3hmUUV1WG9iWmdJMnluM1NnCjBtcXhJU1lQ\nYnpqa2N2OGYvLy82Vkc2bnZqV2tQeFpFZDF5UEhqaDJWc0UKLS0tIG0xbVVEYkRq\nNStQV0NKbkFzMzFZWVRhUU9lQmZDSGFSWktqNmVqaWJPQmMKC/ANlnyPQDd/Q5+8\nzZ8mwLhBuClRIthySJCcd1f6G142I3GgnVQXSWYJi7JMmzuwlhCoifCf0LuHlvHH\nKy//bw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuzbgoXZccZ2w/9HGgUyT9nJH7lG2/jfQCZJudY4yAN"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSRFkrV1RaZzFqNDFURysz\nV1hDNE9hUVJYTVZNc2FESllBcDF0R1JTT3pNClhIc29ZMnBRMXQ4WS82TXR2eXY1\nK2d4RUxCNmRPMkR6VjE4MVJDbWF5dVEKLS0tIG0wa0tVOHhJNnZCYTZheGRsNjNx\nR1EyOElCbW83UmI4cnZ2ZDBNSDZmSTAKzhoa3XgPMmJkvk5Z6G/ED5omE8fqYDNN\nr4v0bAe1kz3sYrEjxiUzgiHAsC+24azXoelEEWVXJ4hFBO8KQc/4ZQ==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBySGlDQkdhNWpOR0QwQUV1\nQldnVFdjdGhoa2xFOVVMekMyUW1vekhGMGdvCm5QaEF4UFNMcGdKUHJFV0YwYy9w\nOUovUlc2UUgvRUNFZG4zMXFrUzlEdXMKLS0tIDVQcVdBZk96WXVRSjZhbzZ4ZGpU\nSkNFK2NQaVppdktRN0s1NkV4VlpVVlUKZS4xGa3lD2s5iV9Xir/UIv7R4rw8122p\n0zIwG6cRSYpCHP6eUJsJnxDFlUWZL4sBHh9UDoUQ4Bz0M8Vzap3ZbQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age16rkvks3tljju3y6xu0l7luhjzx634et97g3xe58xf2dgfn2865rqkq6t8f"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDRTOW45QSBtSVBJ\nTjk1b2tiazFjakJuMFBrUEgzaU5pQlZ0ekhiTEhpclYzU2QyWEFBClh3R3F4b3cx\na1J6cWswYlNIZVdoQnNjZnBCdjlVNDM2OXViRnc3WXAzZWcKLS0tIC9mRG1ZbFA5\nd3YrTDRENERIVTlFT1dpeEd4Z0VNYzBUdnVabmJwOTNpTjQKsWG6f4qZo5THQAq+\n5gYc/Urzo7CEfJvo2uHNIr5aamMV8UFdPPiK6KKrH98gv8L9zXMfMciDoxMgIrzu\nN+dB4g==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDRTOW45QSBsSjUv\nNktkam1zaktuZ3BwbjdUb3k5WWsxVk1aN1Z0Tm45TmRlYk4xMFhnCjZZQ293ZTFy\nVVdzdkJqM0Q5cWZSOHpmdXoyQUJTZVFzZnJFMmRNbzhwQjAKLS0tIGZmZ2drZFlI\ndFV1YkxWSHVGUVRKays2RS9JRFYweldlVnkvU0VoRjlBQXMKDxyqFP1Y3TBcR3uV\nfKChCQZrdy3l2CeZtx9P74BbW8vaO8oRcfigAki8a3JplNmX+UmKJIcbw5nKeU3y\nMwkBtQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFStro05R+CPmjWMHWtzXUKfGll+OosoZtXAyPtngN7T"
},
{
- "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVucGlodyBoQk1s\nRjBJWlZIREFQbXJyVjh6eWdBZlVWWXFyRm45YjJEY2IxNjlTMVZNCnZ6dnMxNkYy\nKzB4UGlhQWY2Qzd4enVEdG1pS3NDMG9FZS9FNkdYdDBaSGMKLS0tIGIvblU1T2h1\nUGx0dnZ0bTlsS21xY2dNVTNnQ3B6NUFuckhwSkFNK3grbTQKp9RWavllfPmzitez\nTWTtcC7nuxiOuoac9Os+Pvuzj3j0B4BxxLNAT7vSVtlgOMl5Mfa5BOvNkcgo6o/x\npJPesA==\n-----END AGE ENCRYPTED FILE-----\n",
+ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVucGlodyBqcWtJ\nbHNaV0lXczB6SiszNEl1Uk5PajBoRlhwWkUxVytsQitrZ2NKRGpZCnQ0YzNhTkwy\nc2tDWEZOT21VUENPcVU5alZBdGVrZWZUbzhtVm5oUDdoQWcKLS0tIFBCK0ZPMVdV\nVHBpNFBwM0N5TFRRcFVZbG9sT3pkWm05cm9OdjRqVER0enMKWXQYx35T61ncNCdc\nD69DfTW8TMX+fqeczScpd7II+Rai5FRa5l7mev/zNx4rLLU5DmfocYBuzcTEC6mj\nk8xfUw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO5qner13Q0fm5NhdXMx2nkt5kxjC0/SVY2FXh01OiHN"
}
],
- "lastmodified": "2026-04-29T10:06:36Z",
- "mac": "ENC[AES256_GCM,data:kroqMERyYthC87raUeryIkgSrxTRvbx9/t4ZD4x27EKQEqpy6SzhIY1bOtoGvROrHf3gtrQ6ZKsQIVeXHRkrZCAchaM2Zf+NhOvwypbxsiy0RNnZtYNidNIH/ByELpUbCSVe9SqNPubFm/fWe0V2FdsmtpNW+kcrFAaey350+fM=,iv:EYphE4seDJuZk6ZW16qfn5W6sOu46ju1Udveqv3i5Tk=,tag:3xuRpDyAlfxOmLac2Di6CA==,type:str]",
+ "lastmodified": "2026-04-29T11:18:55Z",
+ "mac": "ENC[AES256_GCM,data:4WqU/2SV/QoQ3sN9MWhdDJ573qe7KmyB5TNY78laXCQ4p4HIhcYTEqDl9BD556OkECwSawtP7ZES9js0ywU3YNmVd9axyGWIdiR41OSXjCagnfcFg6KJf6njQePpV35hR/ZyzXNrSDnIijo1QOgGUTcifG9yI2UIlJfCQockE+k=,iv:ifzf2V1978sZB2kdlWB9INXkGhRb8oDOPjIaHD+uYPo=,tag:3O0ja7T78q+H2K4X3OY79g==,type:str]",
"unencrypted_comment_regex": ".*",
"version": "3.11.0"
}