@sx4im/skillcheck 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,644 @@
1
+ # Skillcheck Dashboard Plan
2
+
3
+ This document is the build plan for a separate Skillcheck Cloud dashboard and API. The goal is:
4
+
5
+ - Users install the CLI with one command.
6
+ - On first CLI run, users paste the Skillcheck API URL.
7
+ - Users can sign up in a dashboard.
8
+ - Users can create a Skillcheck token.
9
+ - The CLI calls your backend, not the upstream model provider directly.
10
+ - Your upstream provider secret stays only on the backend.
11
+
12
+ ## Target Deployment
13
+
14
+ Use two separate deploys:
15
+
16
+ ```text
17
+ Vercel
18
+ app.skillcheck.yourdomain.com
19
+ Next.js dashboard, marketing, auth pages, API key management, usage UI
20
+
21
+ Render
22
+ api.skillcheck.yourdomain.com
23
+ Node/Fastify or Hono API, OpenAI-compatible proxy, rate limits, usage tracking
24
+
25
+ Postgres
26
+ Render Postgres or Neon/Supabase Postgres
27
+ Users, API keys, usage events, runs, billing state
28
+
29
+ Redis
30
+ Upstash Redis or Render Key Value
31
+ Fast per-token and per-IP rate limits
32
+ ```
33
+
34
+ The CLI setup URL should be:
35
+
36
+ ```bash
37
+ https://api.skillcheck.yourdomain.com/v1
38
+ ```
39
+
40
+ ## Recommended Repos
41
+
42
+ Build this separately from the CLI repo.
43
+
44
+ ```text
45
+ skillcheck-dashboard/
46
+ apps/web/ Next.js dashboard deployed to Vercel
47
+ apps/api/ Node API deployed to Render
48
+ packages/db/ Prisma/Drizzle schema and migrations
49
+ packages/shared/ shared types, token helpers, validation schemas
50
+ ```
51
+
52
+ If you want to move fast, a single repo with `apps/web` and `apps/api` is better than two repos because shared types and database migrations stay together.
53
+
54
+ ## Product Flow
55
+
56
+ ### User Flow
57
+
58
+ 1. User installs CLI:
59
+
60
+ ```bash
61
+ npm install -g @sx4im/skillcheck
62
+ ```
63
+
64
+ 2. User signs up on the dashboard.
65
+ 3. Dashboard shows onboarding:
66
+
67
+ ```bash
68
+ skillcheck setup
69
+ ```
70
+
71
+ 4. User pastes:
72
+
73
+ ```bash
74
+ https://api.skillcheck.yourdomain.com/v1
75
+ ```
76
+
77
+ 5. If private beta, dashboard also gives a token:
78
+
79
+ ```bash
80
+ export SKILLCHECK_TOKEN=sk_live_...
81
+ ```
82
+
83
+ 6. User runs:
84
+
85
+ ```bash
86
+ skillcheck
87
+ ```
88
+
89
+ 7. CLI opens file/folder picker and calls your backend.
90
+ 8. Dashboard usage page shows requests, tokens, cost estimate, verdict counts, and recent runs.
91
+
92
+ ### Admin Flow
93
+
94
+ 1. Admin logs into dashboard.
95
+ 2. Admin sees total usage, active users, failed requests, spend estimate.
96
+ 3. Admin can revoke tokens, ban abusive IPs, adjust quotas, and inspect error logs.
97
+
98
+ ## Frontend: Vercel Dashboard
99
+
100
+ Use Next.js App Router.
101
+
102
+ Recommended stack:
103
+
104
+ ```text
105
+ Next.js
106
+ TypeScript
107
+ Tailwind CSS
108
+ shadcn/ui or custom components
109
+ Clerk for auth if you want fastest launch
110
+ Stripe later for billing
111
+ PostHog or simple internal analytics later
112
+ ```
113
+
114
+ Pages:
115
+
116
+ ```text
117
+ /
118
+ Marketing page: what Skillcheck does, install command, demo result card.
119
+
120
+ /login
121
+ Auth page.
122
+
123
+ /onboarding
124
+ Shows setup commands and asks user to create first token.
125
+
126
+ /dashboard
127
+ Overview: total checks, pass/fail/verdict mix, monthly usage, active token.
128
+
129
+ /tokens
130
+ Create, list, revoke Skillcheck tokens.
131
+ Show token only once after creation.
132
+
133
+ /usage
134
+ Table of requests with date, token prefix, model, status, prompt tokens, completion tokens, cost estimate.
135
+
136
+ /runs
137
+ Optional later: user-submitted check summaries if CLI uploads metadata.
138
+
139
+ /settings
140
+ User profile, org settings, billing, delete account.
141
+
142
+ /admin
143
+ Admin-only usage, errors, users, abuse controls.
144
+ ```
145
+
146
+ Vercel environment variables:
147
+
148
+ ```bash
149
+ NEXT_PUBLIC_API_URL=https://api.skillcheck.yourdomain.com
150
+ AUTH_SECRET=...
151
+ CLERK_SECRET_KEY=...
152
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...
153
+ DATABASE_URL=...
154
+ ```
155
+
156
+ If you use Clerk, do not expose backend admin secrets in the browser. Only expose `NEXT_PUBLIC_*` variables that are safe for the client.
157
+
158
+ ## Backend: Render API
159
+
160
+ Use Node.js with Fastify or Hono. Fastify is a good default for this API because plugins, hooks, and logging are straightforward.
161
+
162
+ Core responsibility:
163
+
164
+ ```text
165
+ Accept CLI OpenAI-compatible requests.
166
+ Authenticate Skillcheck tokens.
167
+ Rate limit by token and IP.
168
+ Forward requests to upstream provider.
169
+ Track usage and errors.
170
+ Return OpenAI-compatible responses to the CLI.
171
+ ```
172
+
173
+ Render environment variables:
174
+
175
+ ```bash
176
+ PORT=10000
177
+ DATABASE_URL=postgres://...
178
+ REDIS_URL=redis://...
179
+
180
+ FRONTEND_ORIGIN=https://app.skillcheck.yourdomain.com
181
+
182
+ SKILLCHECK_TOKEN_PEPPER=long-random-secret
183
+ ALLOW_ANONYMOUS=false
184
+ ANONYMOUS_DAILY_LIMIT=20
185
+
186
+ PROVIDER_BASE_URL=https://integrate.api.nvidia.com/v1
187
+ PROVIDER_API_KEY=...
188
+ DEFAULT_MODEL=minimaxai/minimax-m2.7
189
+
190
+ REQUEST_TIMEOUT_MS=120000
191
+ REQUEST_DELAY_MS=5000
192
+ MAX_ATTEMPTS=8
193
+ MAX_RETRY_DELAY_MS=60000
194
+ ```
195
+
196
+ Only the Render API should know `PROVIDER_API_KEY`.
197
+
198
+ ## Backend Endpoints
199
+
200
+ ### OpenAI-compatible CLI endpoint
201
+
202
+ The CLI already expects this:
203
+
204
+ ```http
205
+ POST /v1/chat/completions
206
+ Authorization: Bearer sk_live_...
207
+ Content-Type: application/json
208
+ ```
209
+
210
+ Backend behavior:
211
+
212
+ 1. Parse authorization header.
213
+ 2. If token is missing:
214
+ - If `ALLOW_ANONYMOUS=true`, apply strict IP rate limit.
215
+ - Otherwise return `401`.
216
+ 3. Hash provided token and find it in `api_keys`.
217
+ 4. Check revoked status.
218
+ 5. Rate limit by key and IP.
219
+ 6. Forward request body to upstream provider.
220
+ 7. Record request usage.
221
+ 8. Return upstream response unchanged where possible.
222
+
223
+ ### Dashboard API endpoints
224
+
225
+ ```http
226
+ GET /health
227
+ GET /api/me
228
+ GET /api/usage
229
+ GET /api/tokens
230
+ POST /api/tokens
231
+ DELETE /api/tokens/:id
232
+ GET /api/runs
233
+ POST /api/runs
234
+ GET /api/admin/users
235
+ GET /api/admin/usage
236
+ POST /api/admin/tokens/:id/revoke
237
+ ```
238
+
239
+ Dashboard endpoints can live in the Render API, not Vercel serverless, to keep all auth, DB, and token logic in one backend.
240
+
241
+ ## Database Schema
242
+
243
+ Use Postgres.
244
+
245
+ ```sql
246
+ create table users (
247
+ id uuid primary key default gen_random_uuid(),
248
+ email text unique not null,
249
+ name text,
250
+ auth_provider text not null,
251
+ auth_provider_id text not null,
252
+ created_at timestamptz not null default now(),
253
+ updated_at timestamptz not null default now()
254
+ );
255
+
256
+ create table organizations (
257
+ id uuid primary key default gen_random_uuid(),
258
+ name text not null,
259
+ slug text unique not null,
260
+ owner_user_id uuid not null references users(id),
261
+ created_at timestamptz not null default now()
262
+ );
263
+
264
+ create table memberships (
265
+ id uuid primary key default gen_random_uuid(),
266
+ organization_id uuid not null references organizations(id),
267
+ user_id uuid not null references users(id),
268
+ role text not null check (role in ('owner', 'admin', 'member')),
269
+ created_at timestamptz not null default now(),
270
+ unique (organization_id, user_id)
271
+ );
272
+
273
+ create table api_keys (
274
+ id uuid primary key default gen_random_uuid(),
275
+ organization_id uuid not null references organizations(id),
276
+ name text not null,
277
+ prefix text not null,
278
+ token_hash text unique not null,
279
+ last_four text not null,
280
+ created_by_user_id uuid not null references users(id),
281
+ created_at timestamptz not null default now(),
282
+ last_used_at timestamptz,
283
+ revoked_at timestamptz
284
+ );
285
+
286
+ create table usage_events (
287
+ id uuid primary key default gen_random_uuid(),
288
+ organization_id uuid references organizations(id),
289
+ api_key_id uuid references api_keys(id),
290
+ request_id text,
291
+ path text not null,
292
+ model text,
293
+ status_code integer not null,
294
+ prompt_tokens integer not null default 0,
295
+ completion_tokens integer not null default 0,
296
+ total_tokens integer not null default 0,
297
+ estimated_cost_usd numeric(12, 6) not null default 0,
298
+ ip_hash text,
299
+ error_code text,
300
+ created_at timestamptz not null default now()
301
+ );
302
+
303
+ create table skill_runs (
304
+ id uuid primary key default gen_random_uuid(),
305
+ organization_id uuid references organizations(id),
306
+ api_key_id uuid references api_keys(id),
307
+ skill_name text,
308
+ skill_format text,
309
+ verdict text,
310
+ effect_pp numeric,
311
+ with_skill_pass numeric,
312
+ no_skill_pass numeric,
313
+ tasks integer,
314
+ trials integer,
315
+ created_at timestamptz not null default now()
316
+ );
317
+
318
+ create table audit_logs (
319
+ id uuid primary key default gen_random_uuid(),
320
+ organization_id uuid references organizations(id),
321
+ user_id uuid references users(id),
322
+ action text not null,
323
+ metadata jsonb not null default '{}',
324
+ created_at timestamptz not null default now()
325
+ );
326
+ ```
327
+
328
+ ## Token Design
329
+
330
+ Token format:
331
+
332
+ ```text
333
+ sk_live_<random>
334
+ sk_test_<random>
335
+ ```
336
+
337
+ Token generation:
338
+
339
+ ```text
340
+ random = 32 bytes from crypto.randomBytes
341
+ token = sk_live_ + base64url(random)
342
+ prefix = first 12 visible characters
343
+ last_four = last 4 characters
344
+ token_hash = sha256(token + SKILLCHECK_TOKEN_PEPPER)
345
+ ```
346
+
347
+ Rules:
348
+
349
+ - Show token only once after creation.
350
+ - Store only `token_hash`, prefix, and last four.
351
+ - Never log full tokens.
352
+ - Redact auth headers from logs.
353
+ - Let users revoke tokens instantly.
354
+
355
+ ## Rate Limits
356
+
357
+ Start with simple quotas:
358
+
359
+ ```text
360
+ Free anonymous:
361
+ 5 checks per IP per day
362
+
363
+ Logged-in free:
364
+ 50 checks per org per month
365
+ 10 requests per minute
366
+
367
+ Paid:
368
+ Plan-based monthly quota
369
+ 30 requests per minute
370
+ ```
371
+
372
+ Implement with Redis token buckets:
373
+
374
+ ```text
375
+ ratelimit:ip:<hash>:day
376
+ ratelimit:key:<api_key_id>:minute
377
+ ratelimit:org:<org_id>:month
378
+ ```
379
+
380
+ If Redis is unavailable, fail closed for anonymous traffic and fail soft for authenticated paid users with logging.
381
+
382
+ ## Proxy Logic
383
+
384
+ For `/v1/chat/completions`:
385
+
386
+ 1. Accept the CLI request body.
387
+ 2. Force or override model if needed:
388
+
389
+ ```ts
390
+ body.model = process.env.DEFAULT_MODEL;
391
+ ```
392
+
393
+ 3. Forward to upstream provider:
394
+
395
+ ```http
396
+ POST ${PROVIDER_BASE_URL}/chat/completions
397
+ Authorization: Bearer ${PROVIDER_API_KEY}
398
+ ```
399
+
400
+ 4. Return upstream response as-is.
401
+ 5. Record usage from `response.usage`.
402
+
403
+ Do not stream in v1. The current CLI sends `stream: false`, so non-streaming is enough.
404
+
405
+ ## Security Checklist
406
+
407
+ - Store provider secrets only in Render environment variables.
408
+ - Store dashboard auth secrets only in Vercel or Render env vars as needed.
409
+ - Never put provider secrets in CLI, frontend code, or public docs.
410
+ - Hash Skillcheck tokens before storing.
411
+ - Use HTTPS only in production.
412
+ - CORS allow only:
413
+
414
+ ```text
415
+ https://app.skillcheck.yourdomain.com
416
+ ```
417
+
418
+ - CLI requests do not need browser CORS, but dashboard API calls do.
419
+ - Log request IDs, not full payloads.
420
+ - Redact prompts by default in production logs.
421
+ - Add abuse controls before public launch.
422
+
423
+ ## Vercel Deployment Plan
424
+
425
+ 1. Create `skillcheck-dashboard` repo.
426
+ 2. Add `apps/web` Next.js app.
427
+ 3. Push to GitHub.
428
+ 4. Import repo in Vercel.
429
+ 5. Set root directory to `apps/web`.
430
+ 6. Add environment variables.
431
+ 7. Set production domain:
432
+
433
+ ```text
434
+ app.skillcheck.yourdomain.com
435
+ ```
436
+
437
+ 8. Add dashboard pages.
438
+ 9. Add auth.
439
+ 10. Add API token UI.
440
+
441
+ Official note: Vercel environment variables are configured outside source code and apply to new deployments, not old deployments: https://vercel.com/docs/projects/environment-variables
442
+
443
+ ## Render Deployment Plan
444
+
445
+ 1. Add `apps/api` Node service.
446
+ 2. Add `Dockerfile` or simple Node build/start commands.
447
+ 3. Create Render Web Service from GitHub repo.
448
+ 4. Set root directory to `apps/api`.
449
+ 5. Set build command:
450
+
451
+ ```bash
452
+ npm install && npm run build
453
+ ```
454
+
455
+ 6. Set start command:
456
+
457
+ ```bash
458
+ npm run start
459
+ ```
460
+
461
+ 7. Add Render Postgres.
462
+ 8. Add Redis/Key Value.
463
+ 9. Add environment variables.
464
+ 10. Set production domain:
465
+
466
+ ```text
467
+ api.skillcheck.yourdomain.com
468
+ ```
469
+
470
+ Official notes:
471
+
472
+ - Render environment variables are configured per service: https://render.com/docs/configure-environment-variables/
473
+ - Render Postgres can host the production database: https://render.com/docs/postgresql
474
+
475
+ ## MVP Build Order
476
+
477
+ ### Phase 1: Working Proxy
478
+
479
+ Goal: CLI can run against your backend.
480
+
481
+ - Create `apps/api`.
482
+ - Implement `GET /health`.
483
+ - Implement `POST /v1/chat/completions`.
484
+ - Add provider forwarding.
485
+ - Add request timeout and retry.
486
+ - Deploy API to Render.
487
+ - Run:
488
+
489
+ ```bash
490
+ skillcheck setup
491
+ skillcheck check path/to/SKILL.md
492
+ ```
493
+
494
+ ### Phase 2: Token Auth
495
+
496
+ Goal: users need a Skillcheck token.
497
+
498
+ - Add Postgres.
499
+ - Add `api_keys` table.
500
+ - Add token generation endpoint.
501
+ - Verify `Authorization: Bearer ...`.
502
+ - Add usage logging.
503
+ - Add revoke endpoint.
504
+
505
+ ### Phase 3: Dashboard
506
+
507
+ Goal: users can self-serve.
508
+
509
+ - Create `apps/web`.
510
+ - Add auth.
511
+ - Add onboarding page.
512
+ - Add create token page.
513
+ - Add usage page.
514
+ - Deploy to Vercel.
515
+
516
+ ### Phase 4: Abuse and Billing
517
+
518
+ Goal: safe public launch.
519
+
520
+ - Add Redis rate limits.
521
+ - Add org monthly quotas.
522
+ - Add admin dashboard.
523
+ - Add Stripe subscriptions.
524
+ - Add email alerts for quota exceeded and suspicious usage.
525
+
526
+ ### Phase 5: Run History
527
+
528
+ Goal: dashboard becomes useful beyond API keys.
529
+
530
+ - Add optional CLI metadata upload.
531
+ - Store `skill_runs`.
532
+ - Show verdict trend by skill.
533
+ - Show helps/placebo/harms counts.
534
+ - Add shareable run page.
535
+
536
+ ## Suggested Backend File Structure
537
+
538
+ ```text
539
+ apps/api/src/
540
+ index.ts
541
+ env.ts
542
+ db.ts
543
+ auth/
544
+ tokens.ts
545
+ middleware.ts
546
+ routes/
547
+ health.ts
548
+ chat-completions.ts
549
+ tokens.ts
550
+ usage.ts
551
+ runs.ts
552
+ admin.ts
553
+ services/
554
+ provider.ts
555
+ rate-limit.ts
556
+ usage.ts
557
+ audit.ts
558
+ lib/
559
+ hash.ts
560
+ errors.ts
561
+ logger.ts
562
+ ```
563
+
564
+ ## Suggested Frontend File Structure
565
+
566
+ ```text
567
+ apps/web/app/
568
+ page.tsx
569
+ login/page.tsx
570
+ onboarding/page.tsx
571
+ dashboard/page.tsx
572
+ tokens/page.tsx
573
+ usage/page.tsx
574
+ runs/page.tsx
575
+ settings/page.tsx
576
+ admin/page.tsx
577
+
578
+ apps/web/components/
579
+ AppShell.tsx
580
+ UsageChart.tsx
581
+ TokenTable.tsx
582
+ CreateTokenDialog.tsx
583
+ CopyCommand.tsx
584
+ RunCard.tsx
585
+ ```
586
+
587
+ ## MVP API Pseudocode
588
+
589
+ ```ts
590
+ app.post('/v1/chat/completions', async (req, reply) => {
591
+ const token = getBearerToken(req.headers.authorization);
592
+ const auth = await authenticateTokenOrAnonymous(token, req.ip);
593
+
594
+ await rateLimit(auth);
595
+
596
+ const startedAt = Date.now();
597
+ const upstream = await fetch(`${env.PROVIDER_BASE_URL}/chat/completions`, {
598
+ method: 'POST',
599
+ headers: {
600
+ authorization: `Bearer ${env.PROVIDER_API_KEY}`,
601
+ 'content-type': 'application/json'
602
+ },
603
+ body: JSON.stringify({
604
+ ...req.body,
605
+ model: env.DEFAULT_MODEL,
606
+ stream: false
607
+ })
608
+ });
609
+
610
+ const text = await upstream.text();
611
+ await recordUsage({
612
+ auth,
613
+ statusCode: upstream.status,
614
+ responseText: text,
615
+ latencyMs: Date.now() - startedAt
616
+ });
617
+
618
+ reply
619
+ .status(upstream.status)
620
+ .header('content-type', upstream.headers.get('content-type') ?? 'application/json')
621
+ .send(text);
622
+ });
623
+ ```
624
+
625
+ ## What To Build First
626
+
627
+ Build this order:
628
+
629
+ 1. Render API with `/health`.
630
+ 2. Render API `/v1/chat/completions` proxy.
631
+ 3. Point local CLI to Render API with `skillcheck setup`.
632
+ 4. Add Postgres token table.
633
+ 5. Add dashboard token creation.
634
+ 6. Add usage logging.
635
+ 7. Add rate limits.
636
+ 8. Add billing/admin later.
637
+
638
+ Do not start with billing or a polished dashboard. First prove:
639
+
640
+ ```text
641
+ user installs CLI -> runs setup -> checks skill -> backend records usage
642
+ ```
643
+
644
+ That is the real MVP.
@@ -0,0 +1,15 @@
1
+ # Skillcheck dashboard
2
+
3
+ A single static file (`index.html`) where a user pastes their Skillcheck API URL and gets the commands to start using the CLI, a connection test, and an in-browser "with vs without skill" preview.
4
+
5
+ ```bash
6
+ # open it directly
7
+ xdg-open index.html # macOS: open index.html · Windows: start index.html
8
+
9
+ # or serve it
10
+ npx --yes serve .
11
+ ```
12
+
13
+ Nothing to build or configure — the API URL and optional token are entered at runtime and stored in the browser's localStorage.
14
+
15
+ The live preview calls `POST <apiUrl>/chat/completions`, so the API must allow the page's origin via CORS. The bundled `../nvidia-proxy` already returns `access-control-allow-origin: *`, so the preview works even from `file://`. See [`../../dashboard.md`](../../dashboard.md) for the full write-up and [`../../docs/skillcheck-cloud.md`](../../docs/skillcheck-cloud.md) for the API contract.