feat: add URL-driven workspace state and streamed Codex research

This commit is contained in:
2026-09-28 20:08:28 +09:00
parent 85d23a328b
commit 8085ad2f90
207 changed files with 15287 additions and 16026 deletions
+24 -27
View File
@@ -1,45 +1,42 @@
type AccessConfig = { origin: string } & (
| { mode: 'tailscale'; allowedLogin: string }
| { mode: 'none' }
)
| { mode: "tailscale"; allowedLogin: string }
| { mode: "none" }
);
export function readAccessConfig(): AccessConfig | null {
const origin = process.env.TWITTER_LITE_ORIGIN
const mode = process.env.TWITTER_LITE_AUTH_MODE ?? 'tailscale'
const allowedLogin = process.env.TWITTER_LITE_ALLOWED_LOGIN
if (!origin || (mode !== 'tailscale' && mode !== 'none')) return null
const origin = process.env.TWITTER_LITE_ORIGIN;
const mode = process.env.TWITTER_LITE_AUTH_MODE ?? "tailscale";
const allowedLogin = process.env.TWITTER_LITE_ALLOWED_LOGIN;
if (!origin || (mode !== "tailscale" && mode !== "none")) return null;
try {
const url = new URL(origin)
const secure = url.protocol === 'https:'
const local = url.protocol === 'http:' && url.hostname === '127.0.0.1'
if ((!secure && !local) || url.origin !== origin) return null
if (mode === 'none') return { origin, mode }
if (!allowedLogin?.trim()) return null
return { origin, mode, allowedLogin }
const url = new URL(origin);
const secure = url.protocol === "https:";
const local = url.protocol === "http:" && url.hostname === "127.0.0.1";
if ((!secure && !local) || url.origin !== origin) return null;
if (mode === "none") return { origin, mode };
if (!allowedLogin?.trim()) return null;
return { origin, mode, allowedLogin };
} catch {
return null
return null;
}
}
/** Use loopback behind Serve for identity, or a private network for mode none. */
export function checkAccess(
request: Request,
config: AccessConfig | null,
): Response | null {
export function checkAccess(request: Request, config: AccessConfig | null): Response | null {
if (!config) {
return new Response('Access configuration is required.', { status: 503 })
return new Response("Access configuration is required.", { status: 503 });
}
if (
config.mode === 'tailscale' &&
request.headers.get('Tailscale-User-Login') !== config.allowedLogin
config.mode === "tailscale" &&
request.headers.get("Tailscale-User-Login") !== config.allowedLogin
) {
return new Response('Forbidden', { status: 403 })
return new Response("Forbidden", { status: 403 });
}
if (
!['GET', 'HEAD', 'OPTIONS'].includes(request.method) &&
request.headers.get('Origin') !== config.origin
!["GET", "HEAD", "OPTIONS"].includes(request.method) &&
request.headers.get("Origin") !== config.origin
) {
return new Response('Forbidden', { status: 403 })
return new Response("Forbidden", { status: 403 });
}
return null
return null;
}
+131 -155
View File
@@ -1,171 +1,147 @@
// @vitest-environment node
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { checkAccess, readAccessConfig } from './policy.server'
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { checkAccess, readAccessConfig } from "./policy.server";
const config = {
mode: 'tailscale' as const,
origin: 'https://deck.invalid',
allowedLogin: '[email protected]',
}
mode: "tailscale" as const,
origin: "https://deck.invalid",
allowedLogin: "[email protected]",
};
beforeEach(() => vi.stubEnv('TWITTER_LITE_AUTH_MODE', undefined))
afterEach(() => vi.unstubAllEnvs())
beforeEach(() => vi.stubEnv("TWITTER_LITE_AUTH_MODE", undefined));
afterEach(() => vi.unstubAllEnvs());
describe('access boundary', () => {
it.each([
undefined,
'tailscale',
])('requires an owner in identity mode: %s', (mode) => {
vi.stubEnv('TWITTER_LITE_AUTH_MODE', mode)
vi.stubEnv('TWITTER_LITE_ORIGIN', config.origin)
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', '')
expect(readAccessConfig()).toBeNull()
})
describe("access boundary", () => {
it.each([undefined, "tailscale"])("requires an owner in identity mode: %s", (mode) => {
vi.stubEnv("TWITTER_LITE_AUTH_MODE", mode);
vi.stubEnv("TWITTER_LITE_ORIGIN", config.origin);
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", "");
expect(readAccessConfig()).toBeNull();
});
it.each([
'',
'off',
'NONE',
])('fails closed for an unknown auth mode: %s', (mode) => {
vi.stubEnv('TWITTER_LITE_AUTH_MODE', mode)
vi.stubEnv('TWITTER_LITE_ORIGIN', config.origin)
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', config.allowedLogin)
expect(
checkAccess(new Request(config.origin), readAccessConfig())?.status,
).toBe(503)
})
it.each(["", "off", "NONE"])("fails closed for an unknown auth mode: %s", (mode) => {
vi.stubEnv("TWITTER_LITE_AUTH_MODE", mode);
vi.stubEnv("TWITTER_LITE_ORIGIN", config.origin);
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", config.allowedLogin);
expect(checkAccess(new Request(config.origin), readAccessConfig())?.status).toBe(503);
});
it('allows private-proxy navigation without an identity only when explicitly configured', () => {
vi.stubEnv('TWITTER_LITE_AUTH_MODE', 'none')
vi.stubEnv('TWITTER_LITE_ORIGIN', config.origin)
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', '')
const access = readAccessConfig()
expect(access).toEqual({ origin: config.origin, mode: 'none' })
expect(checkAccess(new Request(config.origin), access)).toBeNull()
})
it("allows private-proxy navigation without an identity only when explicitly configured", () => {
vi.stubEnv("TWITTER_LITE_AUTH_MODE", "none");
vi.stubEnv("TWITTER_LITE_ORIGIN", config.origin);
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", "");
const access = readAccessConfig();
expect(access).toEqual({ origin: config.origin, mode: "none" });
expect(checkAccess(new Request(config.origin), access)).toBeNull();
});
it('still requires a configured origin without identity authentication', () => {
vi.stubEnv('TWITTER_LITE_AUTH_MODE', 'none')
vi.stubEnv('TWITTER_LITE_ORIGIN', '')
expect(readAccessConfig()).toBeNull()
})
it("still requires a configured origin without identity authentication", () => {
vi.stubEnv("TWITTER_LITE_AUTH_MODE", "none");
vi.stubEnv("TWITTER_LITE_ORIGIN", "");
expect(readAccessConfig()).toBeNull();
});
it.each([
'POST',
'PUT',
'PATCH',
'DELETE',
])('requires exact Origin without identity authentication for %s', (method) => {
const access = { origin: config.origin, mode: 'none' as const }
expect(
checkAccess(new Request(config.origin, { method }), access)?.status,
).toBe(403)
expect(
checkAccess(
new Request(config.origin, {
method,
headers: {
Origin: 'https://other.invalid',
'Sec-Fetch-Site': 'same-origin',
},
}),
access,
)?.status,
).toBe(403)
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { Origin: config.origin },
}),
access,
),
).toBeNull()
})
it.each(["POST", "PUT", "PATCH", "DELETE"])(
"requires exact Origin without identity authentication for %s",
(method) => {
const access = { origin: config.origin, mode: "none" as const };
expect(checkAccess(new Request(config.origin, { method }), access)?.status).toBe(403);
expect(
checkAccess(
new Request(config.origin, {
method,
headers: {
Origin: "https://other.invalid",
"Sec-Fetch-Site": "same-origin",
},
}),
access,
)?.status,
).toBe(403);
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { Origin: config.origin },
}),
access,
),
).toBeNull();
},
);
it('fails closed when the deployment is not configured', () => {
vi.stubEnv('TWITTER_LITE_ORIGIN', '')
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', '')
expect(readAccessConfig()).toBeNull()
expect(checkAccess(new Request(config.origin), null)?.status).toBe(503)
})
it("fails closed when the deployment is not configured", () => {
vi.stubEnv("TWITTER_LITE_ORIGIN", "");
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", "");
expect(readAccessConfig()).toBeNull();
expect(checkAccess(new Request(config.origin), null)?.status).toBe(503);
});
it.each([
'',
'https://deck.invalid/path',
'http://deck.invalid',
'not a URL',
])('rejects an invalid configured origin: %s', (origin) => {
vi.stubEnv('TWITTER_LITE_ORIGIN', origin)
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', config.allowedLogin)
expect(readAccessConfig()).toBeNull()
})
it.each(["", "https://deck.invalid/path", "http://deck.invalid", "not a URL"])(
"rejects an invalid configured origin: %s",
(origin) => {
vi.stubEnv("TWITTER_LITE_ORIGIN", origin);
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", config.allowedLogin);
expect(readAccessConfig()).toBeNull();
},
);
it.each([
'https://deck.invalid',
'http://127.0.0.1:4173',
])('accepts an explicit deployment origin: %s', (origin) => {
vi.stubEnv('TWITTER_LITE_ORIGIN', origin)
vi.stubEnv('TWITTER_LITE_ALLOWED_LOGIN', config.allowedLogin)
expect(readAccessConfig()).toEqual({ ...config, origin })
})
it.each(["https://deck.invalid", "http://127.0.0.1:4173"])(
"accepts an explicit deployment origin: %s",
(origin) => {
vi.stubEnv("TWITTER_LITE_ORIGIN", origin);
vi.stubEnv("TWITTER_LITE_ALLOWED_LOGIN", config.allowedLogin);
expect(readAccessConfig()).toEqual({ ...config, origin });
},
);
it.each([
undefined,
'[email protected]',
'[email protected], [email protected]',
])('rejects absent, foreign, or ambiguous identities: %s', (login) => {
const headers = new Headers()
if (login) headers.set('Tailscale-User-Login', login)
expect(
checkAccess(new Request(config.origin, { headers }), config)?.status,
).toBe(403)
})
it.each([undefined, "[email protected]", "[email protected], [email protected]"])(
"rejects absent, foreign, or ambiguous identities: %s",
(login) => {
const headers = new Headers();
if (login) headers.set("Tailscale-User-Login", login);
expect(checkAccess(new Request(config.origin, { headers }), config)?.status).toBe(403);
},
);
it('permits owner navigation back from an OAuth provider without Origin', () => {
const request = new Request(
`${config.origin}/oauth/mastodon/callback?code=code`,
{
headers: {
'Tailscale-User-Login': config.allowedLogin,
'Sec-Fetch-Site': 'cross-site',
},
it("permits owner navigation back from an OAuth provider without Origin", () => {
const request = new Request(`${config.origin}/oauth/mastodon/callback?code=code`, {
headers: {
"Tailscale-User-Login": config.allowedLogin,
"Sec-Fetch-Site": "cross-site",
},
)
expect(checkAccess(request, config)).toBeNull()
})
});
expect(checkAccess(request, config)).toBeNull();
});
it.each([
'POST',
'PUT',
'PATCH',
'DELETE',
])('requires exact Origin for %s even with same-origin Fetch Metadata', (method) => {
const headers = {
'Tailscale-User-Login': config.allowedLogin,
'Sec-Fetch-Site': 'same-origin',
}
expect(
checkAccess(new Request(config.origin, { method, headers }), config)
?.status,
).toBe(403)
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { ...headers, Origin: 'https://other.invalid' },
}),
config,
)?.status,
).toBe(403)
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { ...headers, Origin: config.origin },
}),
config,
),
).toBeNull()
})
})
it.each(["POST", "PUT", "PATCH", "DELETE"])(
"requires exact Origin for %s even with same-origin Fetch Metadata",
(method) => {
const headers = {
"Tailscale-User-Login": config.allowedLogin,
"Sec-Fetch-Site": "same-origin",
};
expect(checkAccess(new Request(config.origin, { method, headers }), config)?.status).toBe(
403,
);
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { ...headers, Origin: "https://other.invalid" },
}),
config,
)?.status,
).toBe(403);
expect(
checkAccess(
new Request(config.origin, {
method,
headers: { ...headers, Origin: config.origin },
}),
config,
),
).toBeNull();
},
);
});