Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
66 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1import { env } from "cloudflare:workers";
2
3import { type AuditEntry, type AuditQuery, type Viewer, auditClient } from "@g1t/contracts";
4
5import { retainedSince, visibilityFor } from "./audit";
6import { instrumented } from "./perf.server";
7import { billing } from "./services.server";
8import { roleIn } from "./session.server";
9
10/** The audit log, which the events service keeps. */
11export const audit = auditClient(instrumented("events", env.EVENTS));
12
13/**
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo14 * How many days of the workspace's log are kept: 7 for a free workspace,
15 * 90 on the plan, or what g1t staff set for its account. Null when billing
16 * cannot say, and then nothing is held back.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily17 */
18export async function auditRetention(workspace: string): Promise<number | null> {
19 const found = await billing.entitlements(workspace.toLowerCase()).catch(() => null);
20 return found?.auditRetentionDays ?? null;
21}
22
23/** The most rows one export writes. */
24export const EXPORT_LIMIT = 10_000;
25
26/**
27 * Entries of a workspace's log the viewer may see, or null when they may
28 * see none of it.
29 */
30export async function auditPage(viewer: Viewer, query: Omit<AuditQuery, "visibility">) {
31 const visibility = viewer ? visibilityFor(roleIn(viewer, query.workspace), viewer.username) : null;
32 if (!visibility) return null;
33 // Reads and exports go back only as far as the workspace's plan keeps.
34 const days = await auditRetention(query.workspace);
35 const since = days == null ? query.since : retainedSince(query.since, days);
36 return audit.list({ ...query, since, workspace: query.workspace.toLowerCase(), visibility });
37}
38
39/** Every entry matching `query`, page by page, up to `EXPORT_LIMIT`. */
40export async function auditAll(viewer: Viewer, query: Omit<AuditQuery, "visibility">): Promise<AuditEntry[] | null> {
41 const entries: AuditEntry[] = [];
42 let before = query.before ?? null;
43 while (entries.length < EXPORT_LIMIT) {
44 const page = await auditPage(viewer, { ...query, before, limit: 500 });
45 if (!page) return null;
46 entries.push(...page.entries);
47 if (!page.next) break;
48 before = page.next;
49 }
50 return entries.slice(0, EXPORT_LIMIT);
51}
52
53/**
54 * What the given runs did, oldest first, for members of the workspace.
55 * Not narrowed to one repository: an attempt on another is what most
56 * needs to be seen.
57 */
58export async function runAudit(viewer: Viewer, owner: string, runIds: string[]): Promise<AuditEntry[]> {
59 if (runIds.length === 0) return [];
60 const page = await auditPage(viewer, {
61 workspace: owner,
62 runIds: runIds.slice(0, 50),
63 limit: 200,
64 }).catch(() => null);
65 return page ? [...page.entries].reverse() : [];
66}

This file's history is long; its oldest lines are credited to the oldest commit read.