Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
917434a
feat(cli): publish dedicated proxy binary
kwakayama Aug 2, 2026
59fbf93
fix(cli): harden proxy release contract
kwakayama Aug 2, 2026
dcf242a
Keep proxy release checks robust under extension mutation
kojiwakayama Aug 3, 2026
e63d67c
Reconcile the dedicated proxy release with current main
kojiwakayama Aug 3, 2026
4ac1e19
Preserve fork safety in the proxy smoke gate
kojiwakayama Aug 3, 2026
4829857
Keep the proxy dependency lock aligned with current main
kojiwakayama Aug 3, 2026
c8b8761
Keep proxy smoke checks stable during asynchronous startup
kojiwakayama Aug 3, 2026
30254f7
Reconcile the proxy artifact with the released framework baseline
kojiwakayama Aug 3, 2026
e99381d
Keep proxy script tests frozen after baseline reconciliation
kojiwakayama Aug 3, 2026
0a98e7a
Clarify standalone proxy provider activation
kojiwakayama Aug 3, 2026
6811e51
fix(platform): close filesystem hardening review gaps (#3323)
kojiwakayama Aug 3, 2026
0c30c9c
Merge remote-tracking branch 'origin/main' into codex/proxy-specific-…
kojiwakayama Aug 3, 2026
07cd1f4
Diagnose invalid CACHE_TYPE values and mark the rollout shim for removal
kojiwakayama Aug 3, 2026
e6083e7
fix(security): bind hosted source and environment identity (#3290)
kojiwakayama Aug 3, 2026
3192cea
Protect proxy binary release from hidden regressions
kojiwakayama Aug 3, 2026
f56afcd
fix(security): harden rate limits with extension-owned Redis (#3304)
kojiwakayama Aug 3, 2026
e2a93fc
Reject ambiguous SBOM lock argument
kojiwakayama Aug 3, 2026
f63646c
fix(agent): close alias pollution, unverified project lookups, and fa…
kojiwakayama Aug 3, 2026
37e2680
fix(utils): repair hash, id, extension, sleep, namespace, and memoize…
kojiwakayama Aug 3, 2026
c196ffc
fix(cli): harden deploy polling and route verification (#3311)
kojiwakayama Aug 3, 2026
5d107f1
fix(platform): fail closed on GitHub filesystem config errors (#3333)
kojiwakayama Aug 3, 2026
0d20941
Stabilize stuck worker admission regression (#3336)
kojiwakayama Aug 3, 2026
8c2b6e9
docs(security): document hosted identity rollout order and close #329…
kwakayama Aug 3, 2026
c6d924e
Keep proxy packaging from inheriting renderer memory
kojiwakayama Aug 3, 2026
81d739e
Turn the proxy memory fix into verifiable slices
kojiwakayama Aug 3, 2026
64e4b02
Reuse the proven proxy graph instead of the monolith
kojiwakayama Aug 3, 2026
162b8ff
Keep proxy releases available on both Linux architectures
kojiwakayama Aug 3, 2026
52c6c6c
Keep proxy rollout guidance actionable
kojiwakayama Aug 3, 2026
671df97
Reject proxy artifacts that recreate the staging OOM
kojiwakayama Aug 3, 2026
4d9596a
Make the proxy profile safe without caller conventions
kojiwakayama Aug 3, 2026
0a4b950
Preserve corrected proxy rollout guidance before release
kojiwakayama Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ REDIS_URL=

# Proxy Mode (deployed pods)
# Set PROXY_MODE=1 only for hosted/deployed runtimes.
# Requires NODE_ENV=production and a valid signing key.
# Requires NODE_ENV=production, a valid signing key, and a private runtime
# behind an edge that strips client-supplied forwarding/project headers.
# PROXY_MODE=1
# NODE_ENV=production
# CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY=
# Required (exactly "1") for hosted proxy mode; the runtime refuses to boot
# without it. Set it only when the process is private behind a sanitising
# edge. Rollout order for existing deployments: set this variable first, then
# deploy the proxy tier, then the runtime tier. See src/security/README.md
# ("Rollout ordering for hosted identity changes").
# VERYFRONT_TRUST_FORWARDED_HEADERS=1

# Host outbound network policy
# Remote modules and remote MCP calls may reach public HTTP(S) endpoints. The
Expand All @@ -32,6 +39,13 @@ REDIS_URL=
# this host-owned option.
# VERYFRONT_HOST_ALLOW_INTERNAL_EGRESS=1

# Optional privileged environment-value retrieval for shared runtimes.
# Configure both values only when VERYFRONT_API_BASE_URL exposes the canonical
# /internal/project-environment-variables endpoint. The runtime first verifies
# the request-scoped bearer token and never falls back if this endpoint fails.
# VERYFRONT_API_INTERNAL_USER=
# VERYFRONT_API_INTERNAL_PASS=

# Binary compilation
# Set to 1 to always rebuild binary, even if source unchanged
VERYFRONT_BINARY_FRESH=1
Expand Down
71 changes: 69 additions & 2 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -407,41 +407,104 @@ jobs:
# Version comes directly from deno.json
# ============================================

tests-proxy-binary:
if: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-deno
- run: deno task build:prepare
- name: Verify proxy dependency lock is current
run: |
deno task build:proxy-lock
git diff --exit-code -- scripts/build/proxy-deno.lock
- name: Compile proxy binary
run: |
deno run -A scripts/build/compile-binary.ts \
--entrypoint cli/proxy-main.ts \
--profile proxy \
--target x86_64-unknown-linux-gnu \
--output veryfront-proxy-linux-x64
- name: Smoke test proxy binary
run: bash scripts/build/smoke-proxy-binary.sh ./veryfront-proxy-linux-x64
- name: Enforce proxy memory limit
run: bash scripts/build/smoke-proxy-memory.sh ./veryfront-proxy-linux-x64

build-binaries:
if: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.ref == 'refs/heads/main' }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ contains(matrix.os, 'windows') }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
target: aarch64-apple-darwin
name: veryfront-macos-arm64
entrypoint: cli/main.ts
profile: full
- os: macos-latest
target: x86_64-apple-darwin
name: veryfront-macos-x64
entrypoint: cli/main.ts
profile: full
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
name: veryfront-linux-x64
entrypoint: cli/main.ts
profile: full
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
name: veryfront-linux-arm64
entrypoint: cli/main.ts
profile: full
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
name: veryfront-proxy-linux-x64
entrypoint: cli/proxy-main.ts
profile: proxy
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
name: veryfront-proxy-linux-arm64
entrypoint: cli/proxy-main.ts
profile: proxy
- os: windows-2022
target: x86_64-pc-windows-msvc
name: veryfront-windows-x64.exe
entrypoint: cli/main.ts
profile: full
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-deno

- run: deno task build:prepare

- name: Verify proxy dependency lock is current
if: matrix.profile == 'proxy'
shell: bash
run: |
deno task build:proxy-lock
git diff --exit-code -- scripts/build/proxy-deno.lock

- name: Compile binary
shell: bash
run: |
deno run -A scripts/build/compile-binary.ts \
--entrypoint ${{ matrix.entrypoint }} \
--profile ${{ matrix.profile }} \
--target ${{ matrix.target }} \
--output ${{ matrix.name }}

- name: Smoke test proxy binary
if: matrix.name == 'veryfront-proxy-linux-x64'
shell: bash
run: bash scripts/build/smoke-proxy-binary.sh ./${{ matrix.name }}

- name: Enforce proxy memory limit
if: matrix.name == 'veryfront-proxy-linux-x64'
shell: bash
run: bash scripts/build/smoke-proxy-memory.sh ./veryfront-proxy-linux-x64

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.name }}
Expand Down Expand Up @@ -495,7 +558,9 @@ jobs:
- name: Generate SBOM
env:
VERSION: ${{ steps.version.outputs.version }}
run: deno task sbom:all --output-dir "dist/sbom-${VERSION}"
run: |
deno task sbom:all --output-dir "dist/sbom-${VERSION}"
deno task sbom --lock scripts/build/proxy-deno.lock --output "dist/sbom-${VERSION}/proxy.json"

- name: Create release GitHub App token
id: release-app-token
Expand Down Expand Up @@ -647,7 +712,9 @@ jobs:
- name: Generate SBOM
env:
VERSION: ${{ steps.version.outputs.version }}
run: deno task sbom:all --output-dir "dist/sbom-${VERSION}"
run: |
deno task sbom:all --output-dir "dist/sbom-${VERSION}"
deno task sbom --lock scripts/build/proxy-deno.lock --output "dist/sbom-${VERSION}/proxy.json"

- name: Create git tag
env:
Expand Down
38 changes: 22 additions & 16 deletions cli/commands/deploy/command.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { DeploymentRoutingConvergence } from "../../shared/deployment/contr
import { FakeTime } from "#std/testing/time";
import { stripAnsi } from "../../ui/ansi.ts";
import { setVerboseMode } from "../../utils/index.ts";
import { RELEASE_ASSET_MANIFEST_SCHEMA_VERSION } from "veryfront/release-assets";

/**
* The real Deploy Execution module with test-bounded polling: these suites
Expand Down Expand Up @@ -209,20 +210,20 @@ function createDeployFetchHandler(options: {
state: "ready",
manifest_version: 1,
manifest: {
schemaVersion: 1,
schemaVersion: RELEASE_ASSET_MANIFEST_SCHEMA_VERSION,
projectId: PROJECT_ID,
releaseId: RELEASE_ID,
releaseVersion: 41,
manifestVersion: 1,
builderVersion: "test",
sourceContentHash: options.sourceDigest,
sourceContentHash: options.sourceDigest.slice("sha256:".length),
createdAt: "2026-07-10T09:20:00.000Z",
assetBasePath: "/_vf/assets",
modules: {},
css: [],
routes: {},
dependencyMode: "source",
dependencies: {},
fallback: { mode: "jit", gaps: [] },
},
});
}
Expand Down Expand Up @@ -838,13 +839,13 @@ it("uses canonical production read-back in human and JSON modes", async () => {
state: "ready",
manifest_version: 1,
manifest: {
schemaVersion: 1,
schemaVersion: RELEASE_ASSET_MANIFEST_SCHEMA_VERSION,
projectId: PROJECT_ID,
releaseId: RELEASE_ID,
releaseVersion: 41,
manifestVersion: 1,
builderVersion: "test",
sourceContentHash: sourceDigest,
sourceContentHash: sourceDigest.slice("sha256:".length),
createdAt: "2026-07-10T09:20:00.000Z",
assetBasePath: "/_vf/assets",
modules: {
Expand All @@ -858,10 +859,11 @@ it("uses canonical production read-back in human and JSON modes", async () => {
routes: {
"/dashboard": {
modules: ["pages/dashboard.tsx"],
css: [],
},
},
dependencyMode: "source",
dependencies: {},
fallback: { mode: "jit", gaps: [] },
},
});
}
Expand Down Expand Up @@ -1064,7 +1066,10 @@ it("uses canonical production read-back in human and JSON modes", async () => {
await time.tickAsync(0);
for (
let tick = 0;
releaseSourceReads < 20 && tick < 40;
// The deploy flow now does more pre-mutation verification before this
// poll starts. Keep the read budget fixed at 20, but allow enough fake
// clock ticks for the async chain to issue all reads under load.
releaseSourceReads < 20 && tick < 60;
tick++
) {
await time.tickAsync(500);
Expand Down Expand Up @@ -1208,13 +1213,13 @@ it("deploys production from a dirty worktree when the pushed digest matches the
state: "ready",
manifest_version: 1,
manifest: {
schemaVersion: 1,
schemaVersion: RELEASE_ASSET_MANIFEST_SCHEMA_VERSION,
projectId: PROJECT_ID,
releaseId: RELEASE_ID,
releaseVersion: 41,
manifestVersion: 1,
builderVersion: "test",
sourceContentHash: sourceDigest,
sourceContentHash: sourceDigest.slice("sha256:".length),
createdAt: "2026-07-10T09:20:00.000Z",
assetBasePath: "/_vf/assets",
modules: {
Expand All @@ -1228,10 +1233,11 @@ it("deploys production from a dirty worktree when the pushed digest matches the
routes: {
"/dashboard": {
modules: ["pages/dashboard.tsx"],
css: [],
},
},
dependencyMode: "source",
dependencies: {},
fallback: { mode: "jit", gaps: [] },
},
});
}
Expand Down Expand Up @@ -1569,20 +1575,20 @@ it("uses an alternative slug when inferred first deploy project creation conflic
state: "ready",
manifest_version: 1,
manifest: {
schemaVersion: 1,
schemaVersion: RELEASE_ASSET_MANIFEST_SCHEMA_VERSION,
projectId: PROJECT_ID,
releaseId: RELEASE_ID,
releaseVersion: 41,
manifestVersion: 1,
builderVersion: "test",
sourceContentHash: sourceDigest,
sourceContentHash: sourceDigest.slice("sha256:".length),
createdAt: "2026-07-10T09:20:00.000Z",
assetBasePath: "/_vf/assets",
modules: {},
css: [],
routes: {},
dependencyMode: "source",
dependencies: {},
fallback: { mode: "jit", gaps: [] },
},
});
}
Expand Down Expand Up @@ -1754,20 +1760,20 @@ it("collects configured app and pages routes when projectDir has a trailing slas
state: "ready",
manifest_version: 1,
manifest: {
schemaVersion: 1,
schemaVersion: RELEASE_ASSET_MANIFEST_SCHEMA_VERSION,
projectId: PROJECT_ID,
releaseId: RELEASE_ID,
releaseVersion: 41,
manifestVersion: 1,
builderVersion: "test",
sourceContentHash: sourceDigest,
sourceContentHash: sourceDigest.slice("sha256:".length),
createdAt: "2026-07-10T09:20:00.000Z",
assetBasePath: "/_vf/assets",
modules: {},
css: [],
routes: {},
dependencyMode: "source",
dependencies: {},
fallback: { mode: "jit", gaps: [] },
},
}));
}
Expand Down
38 changes: 5 additions & 33 deletions cli/commands/serve/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { exitProcess, registerTerminationSignals, showHeader } from "#cli/utils"
import { generateDefaultProjectId } from "../../utils/project.ts";
import { startCliProductionServer } from "#cli/shared/server-startup";
import { ensureCliBundlerContracts } from "#cli/shared/default-contracts";
import { runStandaloneProxyRuntime } from "./proxy-runtime.ts";

const STARTUP_ERROR_FLUSH_TIMEOUT_MS = 2_000;

Expand Down Expand Up @@ -141,39 +142,10 @@ async function runSplit(options: ServeOptions): Promise<void> {
}

async function runProxy(options: ServeOptions): Promise<void> {
showHeader();
cliLogger.info(`Starting proxy server on ${options.bindAddress}:${options.port}`);

const { setEnv } = await import("veryfront/platform");
setEnv("PORT", String(options.port));
setEnv("HOST", options.bindAddress);

const {
activateStandaloneProxyCacheExtension,
registerStandaloneProxyCacheExtensionTeardown,
} = await import(
"./proxy-extension-composition.ts"
);
const extensionLoader = await activateStandaloneProxyCacheExtension();
const teardownCacheExtension = await registerStandaloneProxyCacheExtensionTeardown(
extensionLoader,
);

// DenoHttpServer.serve() blocks until the server stops,
// so this import keeps the process alive.
try {
await import("veryfront/proxy/main");
} catch (error) {
try {
await teardownCacheExtension();
} catch (cleanupError) {
cliLogger.error("Failed to clean up proxy extensions after startup failure", cleanupError);
}
throw error;
}

// Keep the process alive (Deno.serve returns immediately in compiled binaries)
await new Promise(() => {});
await runStandaloneProxyRuntime({
bindAddress: options.bindAddress,
port: options.port,
});
}

function createDeferredProductionStartupErrorReporter(): {
Expand Down
Loading