Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,14 @@ jobs:
BUILD_VERSION: ${{ env.RELEASE_VERSION }}
run: pnpm run build

- name: Write gateway token
run: printf '%s' "$GATEWAY_TOKEN" > "$RUNNER_TEMP/gateway-token"
env:
GATEWAY_TOKEN: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_POSTHOG_GATEWAY_TOKEN }}

- name: Scan skills with Warlock
env:
CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY: ${{ secrets.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY }}
CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE: ${{ runner.temp }}/gateway-token
run: node scripts/scan-warlock.js dist/skills

- name: List build artifacts
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@ jobs:
- name: Build
run: pnpm build

- name: Write gateway token
run: printf '%s' "$GATEWAY_TOKEN" > "$RUNNER_TEMP/gateway-token"
env:
GATEWAY_TOKEN: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_POSTHOG_GATEWAY_TOKEN }}

- name: Scan skills with Warlock
# Fork PRs don't get repo secrets, so Warlock can't reach the LLM gateway to
# triage matches and reports every hit (incl. false positives) as a threat.
# On forks we let the scan run and annotate, but don't fail the build — the
# full triaged scan still gates trusted runs (push-to-main, release).
continue-on-error: ${{ github.event.pull_request.head.repo.fork == true }}
env:
CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY: ${{ secrets.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY }}
CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE: ${{ runner.temp }}/gateway-token
run: node scripts/scan-warlock.js dist/skills

- name: Lint env var naming conventions
Expand Down
41 changes: 25 additions & 16 deletions scripts/scan-warlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ const isCI = Boolean(process.env.CI);
// matches. The LLM decides whether each match is a real threat or a
// false positive
//
// The gateway's `ci` product serves CI runs with a personal API key. The
// legacy `wizard` product is retired and refuses every caller with a 403.
// US: https://gateway.us.posthog.com/ci
// EU: https://gateway.eu.posthog.com/ci
// Local: http://localhost:3308/ci
// Auth is a pre-issued gateway bearer read from the file named by
// CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE (CI writes the secret there), the
// same mechanism the wizard's CI uses (WIZARD_CI_GATEWAY_TOKEN_FILE).
// The URL carries no path; the SDK appends /v1/messages.
// US: https://ai-gateway.us.posthog.com
// EU: https://ai-gateway.eu.posthog.com
// ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN override both for local runs.

function getGatewayUrl() {
const host = process.env.POSTHOG_HOST || "https://us.posthog.com";
Expand All @@ -88,25 +90,32 @@ function getGatewayUrl() {
}
}

if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
return "http://localhost:3308/ci";
}
if (hostname === "eu.posthog.com" || hostname === "eu.i.posthog.com") {
return "https://gateway.eu.posthog.com/ci";
return "https://ai-gateway.eu.posthog.com";
}
return "https://ai-gateway.us.posthog.com";
}

function readGatewayToken() {
if (process.env.ANTHROPIC_AUTH_TOKEN) return process.env.ANTHROPIC_AUTH_TOKEN;
const tokenFile = process.env.CONTEXT_MILL_WARLOCK_GATEWAY_TOKEN_FILE;
if (!tokenFile) return null;
try {
return fs.readFileSync(tokenFile, "utf8").trim() || null;
} catch {
return null;
}
return "https://gateway.us.posthog.com/ci";
}

function createLLMProvider() {
// Prefer the wizard's local proxy if available (ANTHROPIC_BASE_URL),
// otherwise fall back to the PostHog gateway.
const baseURL = process.env.ANTHROPIC_BASE_URL || getGatewayUrl();
const apiKey = process.env.ANTHROPIC_AUTH_TOKEN || process.env.CONTEXT_MILL_WARLOCK_POSTHOG_PERSONAL_KEY;
if (!apiKey) return null;
const authToken = readGatewayToken();
if (!authToken) return null;

// authToken sends `Authorization: Bearer`; the gateway rejects `x-api-key`.
const client = new Anthropic({
baseURL,
apiKey,
authToken,
});

return async (prompt) => {
Expand Down Expand Up @@ -273,7 +282,7 @@ async function main() {
console.log("LLM triage enabled (using PostHog gateway).\n");
} else {
console.log(
"LLM triage disabled (no API key configured). All matches will be treated as threats.\n",
"LLM triage disabled (no gateway token configured). All matches will be treated as threats.\n",
);
}

Expand Down
Loading