Initial import from garrytan/gstack@026751e (main snapshot via local relay)
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled

Source: https://github.com/garrytan/gstack/commit/026751e
This commit is contained in:
Rocky
2026-05-19 21:18:17 +02:00
commit 834c6db075
797 changed files with 267839 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
-- gstack telemetry schema
-- Tables for tracking usage, installations, and update checks.
-- Main telemetry events (skill runs, upgrades)
CREATE TABLE telemetry_events (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
received_at TIMESTAMPTZ DEFAULT now(),
schema_version INTEGER NOT NULL DEFAULT 1,
event_type TEXT NOT NULL DEFAULT 'skill_run',
gstack_version TEXT NOT NULL,
os TEXT NOT NULL,
arch TEXT,
event_timestamp TIMESTAMPTZ NOT NULL,
skill TEXT,
session_id TEXT,
duration_s NUMERIC,
outcome TEXT NOT NULL,
error_class TEXT,
used_browse BOOLEAN DEFAULT false,
concurrent_sessions INTEGER DEFAULT 1,
installation_id TEXT -- nullable, only for "community" tier
);
-- Index for skill_sequences view performance
CREATE INDEX idx_telemetry_session_ts ON telemetry_events (session_id, event_timestamp);
-- Index for crash clustering
CREATE INDEX idx_telemetry_error ON telemetry_events (error_class, gstack_version) WHERE outcome = 'error';
-- Retention tracking per installation
CREATE TABLE installations (
installation_id TEXT PRIMARY KEY,
first_seen TIMESTAMPTZ DEFAULT now(),
last_seen TIMESTAMPTZ DEFAULT now(),
gstack_version TEXT,
os TEXT
);
-- Install pings from update checks
CREATE TABLE update_checks (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
checked_at TIMESTAMPTZ DEFAULT now(),
gstack_version TEXT NOT NULL,
os TEXT NOT NULL
);
-- RLS: anon key can INSERT and SELECT (all telemetry data is anonymous)
ALTER TABLE telemetry_events ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_insert_only" ON telemetry_events FOR INSERT WITH CHECK (true);
CREATE POLICY "anon_select" ON telemetry_events FOR SELECT USING (true);
ALTER TABLE installations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_insert_only" ON installations FOR INSERT WITH CHECK (true);
CREATE POLICY "anon_select" ON installations FOR SELECT USING (true);
-- Allow upsert (update last_seen)
CREATE POLICY "anon_update_last_seen" ON installations FOR UPDATE USING (true) WITH CHECK (true);
ALTER TABLE update_checks ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_insert_only" ON update_checks FOR INSERT WITH CHECK (true);
CREATE POLICY "anon_select" ON update_checks FOR SELECT USING (true);
-- Crash clustering view
CREATE VIEW crash_clusters AS
SELECT
error_class,
gstack_version,
COUNT(*) as total_occurrences,
COUNT(DISTINCT installation_id) as identified_users, -- community tier only
COUNT(*) - COUNT(installation_id) as anonymous_occurrences, -- events without installation_id
MIN(event_timestamp) as first_seen,
MAX(event_timestamp) as last_seen
FROM telemetry_events
WHERE outcome = 'error' AND error_class IS NOT NULL
GROUP BY error_class, gstack_version
ORDER BY total_occurrences DESC;
-- Skill sequence co-occurrence view
CREATE VIEW skill_sequences AS
SELECT
a.skill as skill_a,
b.skill as skill_b,
COUNT(DISTINCT a.session_id) as co_occurrences
FROM telemetry_events a
JOIN telemetry_events b ON a.session_id = b.session_id
AND a.skill != b.skill
AND a.event_timestamp < b.event_timestamp
WHERE a.event_type = 'skill_run' AND b.event_type = 'skill_run'
GROUP BY a.skill, b.skill
HAVING COUNT(DISTINCT a.session_id) >= 10
ORDER BY co_occurrences DESC;

View File

@@ -0,0 +1,36 @@
-- 002_tighten_rls.sql
-- Lock down read/update access. Keep INSERT policies so old clients can still
-- write via PostgREST while new clients migrate to edge functions.
-- Drop all SELECT policies (anon key should not read telemetry data)
DROP POLICY IF EXISTS "anon_select" ON telemetry_events;
DROP POLICY IF EXISTS "anon_select" ON installations;
DROP POLICY IF EXISTS "anon_select" ON update_checks;
-- Drop dangerous UPDATE policy (was unrestricted on all columns)
DROP POLICY IF EXISTS "anon_update_last_seen" ON installations;
-- Keep INSERT policies — old clients (pre-v0.11.16) still POST directly to
-- PostgREST. These will be dropped in a future migration once adoption of
-- edge-function-based sync is widespread.
-- (anon_insert_only ON telemetry_events — kept)
-- (anon_insert_only ON installations — kept)
-- (anon_insert_only ON update_checks — kept)
-- Explicitly revoke view access (belt-and-suspenders)
REVOKE SELECT ON crash_clusters FROM anon;
REVOKE SELECT ON skill_sequences FROM anon;
-- Keep error_message and failed_step columns (exist on live schema, may be
-- used in future). Add them to the migration record so repo matches live.
ALTER TABLE telemetry_events ADD COLUMN IF NOT EXISTS error_message TEXT;
ALTER TABLE telemetry_events ADD COLUMN IF NOT EXISTS failed_step TEXT;
-- Cache table for community-pulse aggregation (prevents DoS via repeated queries)
CREATE TABLE IF NOT EXISTS community_pulse_cache (
id INTEGER PRIMARY KEY DEFAULT 1,
data JSONB NOT NULL DEFAULT '{}'::jsonb,
refreshed_at TIMESTAMPTZ DEFAULT now()
);
ALTER TABLE community_pulse_cache ENABLE ROW LEVEL SECURITY;
-- No anon policies — only service_role_key (used by edge functions) can read/write

View File

@@ -0,0 +1,25 @@
-- 003_installations_upsert_policy.sql
-- Re-add a scoped UPDATE policy for installations so the telemetry-ingest
-- edge function can upsert (update last_seen) using the caller's anon key
-- instead of the service role key.
--
-- Migration 002 dropped the overly broad "anon_update_last_seen" policy
-- (which allowed UPDATE on ALL columns). This replacement uses:
-- 1. An RLS policy to allow UPDATE (required for any row access)
-- 2. Column-level GRANT to restrict anon to only the tracking columns
-- the edge function actually writes (last_seen, gstack_version, os)
--
-- This means anon callers cannot UPDATE first_seen or installation_id,
-- closing the residual risk from the broad RLS-only approach.
-- RLS policy: allow UPDATE on rows (required for PostgREST/upsert)
CREATE POLICY "anon_update_tracking" ON installations
FOR UPDATE
USING (true)
WITH CHECK (true);
-- Column-level restriction: anon can only UPDATE these three columns.
-- PostgreSQL GRANT UPDATE (col, ...) is enforced at the query level —
-- any UPDATE touching other columns will be rejected with a permission error.
REVOKE UPDATE ON installations FROM anon;
GRANT UPDATE (last_seen, gstack_version, os) ON installations TO anon;

View File

@@ -0,0 +1,44 @@
-- gstack attack telemetry — schema extension for prompt injection events.
--
-- Ships alongside the gstack-telemetry-log `--event-type attack_attempt`
-- flag (bin/gstack-telemetry-log, commits 28ce883c + f68fa4a9). These
-- columns are nullable so the existing skill_run events continue inserting
-- unchanged.
--
-- Fields (1:1 with gstack-telemetry-log flags):
-- security_url_domain — hostname only, never path/query
-- security_payload_hash — salted SHA-256 hex
-- security_confidence — 0..1 numeric, clamped client-side
-- security_layer — stackone_content | testsavant_content
-- | transcript_classifier | aria_regex | canary
-- | deberta_content
-- security_verdict — block | warn | log_only
--
-- Indices:
-- * (security_url_domain, event_timestamp) — for "top domains last 7 days"
-- * (security_layer, event_timestamp) WHERE event_type='attack_attempt'
-- — for layer-distribution queries
--
-- Privacy rules (enforced client-side, documented here):
-- * domain only, never path or query string
-- * payload_hash is a salted hash, not the payload
-- * salt is per-device local file (~/.gstack/security/device-salt) —
-- preventing cross-device rainbow table attacks
ALTER TABLE telemetry_events
ADD COLUMN security_url_domain TEXT,
ADD COLUMN security_payload_hash TEXT,
ADD COLUMN security_confidence NUMERIC,
ADD COLUMN security_layer TEXT,
ADD COLUMN security_verdict TEXT;
-- Top-domains query: ORDER BY count DESC WHERE event_type='attack_attempt'
-- AND event_timestamp > now() - interval '7 days'
CREATE INDEX idx_telemetry_attack_domain
ON telemetry_events (security_url_domain, event_timestamp)
WHERE event_type = 'attack_attempt';
-- Layer-distribution query
CREATE INDEX idx_telemetry_attack_layer
ON telemetry_events (security_layer, event_timestamp)
WHERE event_type = 'attack_attempt';