Skip to content
Open
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
20 changes: 10 additions & 10 deletions src/lib/cloud-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("logs cloud-store resolver (self_hosted client flip)", () => {
test("returns null (local) when mode=local even with url+key present", () => {
const store = resolveLogsCloudStore({
HASNA_LOGS_STORAGE_MODE: "local",
HASNA_LOGS_API_URL: "https://logs.hasna.xyz",
HASNA_LOGS_API_URL: "https://logs.your-deployment.example",
HASNA_LOGS_API_KEY: "k_fake_test_key",
} as NodeJS.ProcessEnv);
expect(store).toBeNull();
Expand All @@ -30,40 +30,40 @@ describe("logs cloud-store resolver (self_hosted client flip)", () => {
expect(() =>
resolveLogsCloudStore({
HASNA_LOGS_STORAGE_MODE: "self_hosted",
HASNA_LOGS_API_URL: "https://logs.hasna.xyz",
HASNA_LOGS_API_URL: "https://logs.your-deployment.example",
} as NodeJS.ProcessEnv),
).toThrow();
});

test("resolves a cloud-http store at <app>.hasna.xyz/v1 when self_hosted + url + key", () => {
test("resolves a cloud-http store at <app>.your-deployment.example/v1 when self_hosted + url + key", () => {
const store = resolveLogsCloudStore({
HASNA_LOGS_STORAGE_MODE: "self_hosted",
HASNA_LOGS_API_URL: "https://logs.hasna.xyz",
HASNA_LOGS_API_URL: "https://logs.your-deployment.example",
HASNA_LOGS_API_KEY: "k_fake_test_key",
} as NodeJS.ProcessEnv);
expect(store).not.toBeNull();
expect(store!.baseUrl).toBe("https://logs.hasna.xyz/v1");
expect(store!.baseUrl).toBe("https://logs.your-deployment.example/v1");
});

test("defaults the base URL to https://logs.hasna.xyz/v1 when only mode+key set", () => {
test("defaults the base URL to https://logs.your-deployment.example/v1 when only mode+key set", () => {
const store = resolveLogsCloudStore({
HASNA_LOGS_STORAGE_MODE: "self_hosted",
HASNA_LOGS_API_KEY: "k_fake_test_key",
} as NodeJS.ProcessEnv);
expect(store).not.toBeNull();
expect(store!.baseUrl).toBe("https://logs.hasna.xyz/v1");
expect(store!.baseUrl).toBe("https://logs.your-deployment.example/v1");
});

// Regression: the fleet flip writes ONLY these two vars (no storage-mode var).
// Presence of both must imply self_hosted and route to cloud-http, otherwise
// an installed CLI silently keeps reading local even with the env set.
test("routes to cloud when only the two flip vars (url + key) are set — no mode var", () => {
const store = resolveLogsCloudStore({
HASNA_LOGS_API_URL: "https://logs.hasna.xyz",
HASNA_LOGS_API_URL: "https://logs.your-deployment.example",
HASNA_LOGS_API_KEY: "k_fake_test_key",
} as NodeJS.ProcessEnv);
expect(store).not.toBeNull();
expect(store!.baseUrl).toBe("https://logs.hasna.xyz/v1");
expect(store!.baseUrl).toBe("https://logs.your-deployment.example/v1");
});

test("key alone (no url, no mode var) stays local — flip writes both vars together", () => {
Expand All @@ -75,7 +75,7 @@ describe("logs cloud-store resolver (self_hosted client flip)", () => {

test("url alone (no key) stays local when no mode var is set", () => {
const store = resolveLogsCloudStore({
HASNA_LOGS_API_URL: "https://logs.hasna.xyz",
HASNA_LOGS_API_URL: "https://logs.your-deployment.example",
} as NodeJS.ProcessEnv);
expect(store).toBeNull();
});
Expand Down
4 changes: 2 additions & 2 deletions src/lib/cloud-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
* When the client-flip contract resolves to `cloud-http` (i.e.
* HASNA_LOGS_STORAGE_MODE=self_hosted AND HASNA_LOGS_API_URL + HASNA_LOGS_API_KEY
* are set), ALL log reads and writes are routed to the app's cloud HTTP API
* (`https://logs.hasna.xyz/v1/logs`) with the bearer key — NOT the local SQLite
* store, NOT a raw DSN.
* (the configured HASNA_LOGS_API_URL, e.g. https://logs.your-deployment.example/v1/logs)
* with the bearer key — NOT the local SQLite store, NOT a raw DSN.
*
* When the flip does not resolve to cloud, this returns `null` and the CLI uses
* its local SQLite store exactly as before (fully reversible: unset the env vars
Expand Down
9 changes: 7 additions & 2 deletions src/server/cloud/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
export interface OpenApiDoc {
openapi: string;
info: { title: string; version: string; description?: string };
servers?: { url: string }[];
servers?: { url: string; description?: string }[];
security?: Record<string, unknown>[];
components: Record<string, unknown>;
paths: Record<string, unknown>;
Expand All @@ -31,7 +31,12 @@ export function buildOpenApiDocument(version: string): OpenApiDoc {
"Cloud API for @hasna/logs — the shared, S3-backed log sink. All /v1 " +
"routes require a Hasna API key (x-api-key or Authorization: Bearer).",
},
servers: [{ url: "https://logs.hasna.xyz" }],
servers: [
{
url: "https://your-deployment.example",
description: "Replace with your self-hosted @hasna/logs deployment's base URL.",
},
],
components: {
securitySchemes: {
apiKey: { type: "apiKey", in: "header", name: "x-api-key" },
Expand Down