docguard-cli 0.18.1 → 0.21.0
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.
- package/README.md +128 -34
- package/cli/commands/demo.mjs +241 -0
- package/cli/commands/guard.mjs +20 -2
- package/cli/commands/init.mjs +122 -0
- package/cli/docguard.mjs +125 -47
- package/cli/validators/canonical-sync.mjs +211 -0
- package/cli/validators/spec-kit.mjs +14 -0
- package/docs/quickstart.md +1 -1
- package/extensions/spec-kit-docguard/README.md +1 -1
- package/extensions/spec-kit-docguard/extension.yml +5 -5
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +1 -1
- package/package.json +1 -1
- package/templates/demo-fixture/.docguard.json +8 -0
- package/templates/demo-fixture/.env.example +5 -0
- package/templates/demo-fixture/AGENTS.md +14 -0
- package/templates/demo-fixture/CHANGELOG.md +13 -0
- package/templates/demo-fixture/DRIFT-LOG.md +3 -0
- package/templates/demo-fixture/README.md +17 -0
- package/templates/demo-fixture/docs-canonical/API-REFERENCE.md +36 -0
- package/templates/demo-fixture/docs-canonical/ARCHITECTURE.md +30 -0
- package/templates/demo-fixture/docs-canonical/DATA-MODEL.md +30 -0
- package/templates/demo-fixture/docs-canonical/ENVIRONMENT.md +20 -0
- package/templates/demo-fixture/docs-canonical/SECURITY.md +15 -0
- package/templates/demo-fixture/docs-canonical/TEST-SPEC.md +10 -0
- package/templates/demo-fixture/package.json +10 -0
- package/templates/demo-fixture/src/api.mjs +18 -0
- package/templates/demo-fixture/src/notifier.mjs +23 -0
- package/templates/demo-fixture/src/scheduler.mjs +8 -0
- package/templates/demo-fixture/src/worker.mjs +15 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
> Note: This demo CHANGELOG is intentionally missing an `[Unreleased]` section
|
|
4
|
+
> so DocGuard's Changelog validator has something to flag.
|
|
5
|
+
|
|
6
|
+
## [1.4.0] - 2026-04-12
|
|
7
|
+
|
|
8
|
+
- Add scheduled retry for failed charges
|
|
9
|
+
- Bump Stripe SDK to v15
|
|
10
|
+
|
|
11
|
+
## [1.3.0] - 2026-03-28
|
|
12
|
+
|
|
13
|
+
- Initial public release
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Acme Payments
|
|
2
|
+
|
|
3
|
+
A payments microservice. Handles charges, refunds, balance lookups.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
- Node.js (ES modules)
|
|
7
|
+
- PostgreSQL
|
|
8
|
+
- Stripe for card processing
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install
|
|
14
|
+
npm start
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
See `docs-canonical/` for the system specs.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
> All requests require `Authorization: Bearer <jwt>` unless noted.
|
|
4
|
+
|
|
5
|
+
## Charges
|
|
6
|
+
|
|
7
|
+
### POST /charge
|
|
8
|
+
Create a new charge.
|
|
9
|
+
|
|
10
|
+
**Request body**
|
|
11
|
+
```json
|
|
12
|
+
{ "amount_cents": 1000, "currency": "USD", "customer_id": "cus_..." }
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Response** — `201 Created` with the charge object.
|
|
16
|
+
|
|
17
|
+
### POST /refund
|
|
18
|
+
Refund a previous charge.
|
|
19
|
+
|
|
20
|
+
**Request body**
|
|
21
|
+
```json
|
|
22
|
+
{ "charge_id": "ch_...", "amount_cents": 1000 }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Balance
|
|
26
|
+
|
|
27
|
+
### GET /balance/:customer_id
|
|
28
|
+
Look up a customer's current balance.
|
|
29
|
+
|
|
30
|
+
**Response**
|
|
31
|
+
```json
|
|
32
|
+
{ "customer_id": "cus_...", "available_cents": 12345 }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
<!-- Demo drift: code also exposes POST /webhooks (Stripe callbacks) but it's
|
|
36
|
+
missing from this reference. DocGuard's API-Surface validator catches it. -->
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ARCHITECTURE — Acme Payments
|
|
2
|
+
|
|
3
|
+
> The system has **3 services**. (Demo drift: code actually has 4 — see `src/`.)
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌───────────┐ queue ┌──────────┐ cron ┌────────────┐
|
|
9
|
+
│ API │ ────────> │ Worker │ <─────── │ Scheduler │
|
|
10
|
+
│ (HTTP) │ │ (jobs) │ │ (timers) │
|
|
11
|
+
└───────────┘ └──────────┘ └────────────┘
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### API
|
|
15
|
+
Handles HTTP requests. Routes live in `src/api.mjs`.
|
|
16
|
+
|
|
17
|
+
### Worker
|
|
18
|
+
Consumes the job queue. Long-running tasks (capture, refund settlement).
|
|
19
|
+
|
|
20
|
+
### Scheduler
|
|
21
|
+
Cron-style triggers for retries and reconciliation.
|
|
22
|
+
|
|
23
|
+
## Data flow
|
|
24
|
+
1. Client POSTs to `/charge` → API validates → enqueues `process_charge` job
|
|
25
|
+
2. Worker dequeues → calls Stripe → writes result to DB
|
|
26
|
+
3. Scheduler reruns failed charges hourly
|
|
27
|
+
|
|
28
|
+
## See also
|
|
29
|
+
- `DATA-MODEL.md` for the persistence layer
|
|
30
|
+
- `SECURITY.md` for auth + secrets handling
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Data Model
|
|
2
|
+
|
|
3
|
+
## charges
|
|
4
|
+
|
|
5
|
+
| Column | Type | Notes |
|
|
6
|
+
|--------|------|-------|
|
|
7
|
+
| `id` | `uuid` (PK) | |
|
|
8
|
+
| `customer_id` | `text` | |
|
|
9
|
+
| `amount_cents` | `bigint` | |
|
|
10
|
+
| `currency` | `text` | ISO-4217 |
|
|
11
|
+
| `status` | `text` | `pending` / `succeeded` / `failed` |
|
|
12
|
+
| `stripe_id` | `text` | nullable |
|
|
13
|
+
| `created_at` | `timestamptz` | default now() |
|
|
14
|
+
|
|
15
|
+
## refunds
|
|
16
|
+
|
|
17
|
+
| Column | Type |
|
|
18
|
+
|--------|------|
|
|
19
|
+
| `id` | `uuid` (PK) |
|
|
20
|
+
| `charge_id` | `uuid` (FK → charges) |
|
|
21
|
+
| `amount_cents` | `bigint` |
|
|
22
|
+
| `created_at` | `timestamptz` |
|
|
23
|
+
|
|
24
|
+
## customers
|
|
25
|
+
|
|
26
|
+
| Column | Type |
|
|
27
|
+
|--------|------|
|
|
28
|
+
| `id` | `text` (PK, `cus_...`) |
|
|
29
|
+
| `email` | `text` |
|
|
30
|
+
| `created_at` | `timestamptz` |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Environment
|
|
2
|
+
|
|
3
|
+
Required environment variables.
|
|
4
|
+
|
|
5
|
+
| Variable | Required | Description |
|
|
6
|
+
|----------|----------|-------------|
|
|
7
|
+
| `DATABASE_URL` | Yes | Postgres connection string |
|
|
8
|
+
| `STRIPE_SECRET_KEY` | Yes | Stripe API key (server-side) |
|
|
9
|
+
| `REDIS_URL` | Yes | Redis URL for the job queue |
|
|
10
|
+
|
|
11
|
+
<!-- Demo drift: REDIS_URL is documented here but missing from .env.example.
|
|
12
|
+
Also, JWT_SECRET is in .env.example + used in code, but not listed here.
|
|
13
|
+
DocGuard's Environment validator catches both. -->
|
|
14
|
+
|
|
15
|
+
## Local development
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cp .env.example .env
|
|
19
|
+
# Fill in the values above
|
|
20
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Authentication
|
|
4
|
+
HTTP API requires `Authorization: Bearer <jwt>`. Tokens are signed with `JWT_SECRET`.
|
|
5
|
+
|
|
6
|
+
## Secrets
|
|
7
|
+
- `STRIPE_SECRET_KEY` — never logged
|
|
8
|
+
- `JWT_SECRET` — rotated quarterly
|
|
9
|
+
|
|
10
|
+
## Threat model
|
|
11
|
+
- Card data is never stored locally — Stripe-tokenized only
|
|
12
|
+
- `JWT_SECRET` rotation invalidates outstanding sessions (acceptable for an internal API)
|
|
13
|
+
|
|
14
|
+
## Audit log
|
|
15
|
+
Every charge / refund writes to the `audit_events` table.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Test Spec
|
|
2
|
+
|
|
3
|
+
## Coverage rules
|
|
4
|
+
- Every route in `src/api.mjs` must have an integration test in `tests/api/`
|
|
5
|
+
- Every worker job must have a unit test in `tests/worker/`
|
|
6
|
+
|
|
7
|
+
## Layers
|
|
8
|
+
- **Unit** — pure logic, no I/O
|
|
9
|
+
- **Integration** — hits a local Postgres + Stripe in test mode
|
|
10
|
+
- **E2E** — against a staging stack (rare; only for release candidates)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// API service — handles HTTP routes.
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
|
|
4
|
+
// Routes intentionally include /webhooks (not in API-REFERENCE.md — demo drift)
|
|
5
|
+
const ROUTES = {
|
|
6
|
+
'POST /charge': createCharge,
|
|
7
|
+
'POST /refund': createRefund,
|
|
8
|
+
'GET /balance/:customer': getBalance,
|
|
9
|
+
'POST /webhooks': handleStripeWebhook, // ← undocumented
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
async function createCharge(req) { /* ... */ }
|
|
13
|
+
async function createRefund(req) { /* ... */ }
|
|
14
|
+
async function getBalance(req) { /* ... */ }
|
|
15
|
+
async function handleStripeWebhook(req) { /* ... */ }
|
|
16
|
+
|
|
17
|
+
const PORT = process.env.PORT || 3000;
|
|
18
|
+
createServer((req, res) => { /* router */ }).listen(PORT);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Notifier service — emails / Slack alerts on big charges or failures.
|
|
2
|
+
// ⚠️ Demo drift: ARCHITECTURE.md only mentions 3 services (API, Worker, Scheduler).
|
|
3
|
+
// This fourth one (Notifier) is in code but missing from the architecture doc.
|
|
4
|
+
// DocGuard's Docs-Diff + Docs-Coverage validators surface this.
|
|
5
|
+
|
|
6
|
+
import { Stripe } from './lib/stripe.mjs';
|
|
7
|
+
|
|
8
|
+
export async function notifyLargeCharge(charge) {
|
|
9
|
+
if (charge.amount_cents > 100000) {
|
|
10
|
+
await sendSlack(`💰 Large charge: $${charge.amount_cents / 100}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function notifyFailure(charge, error) {
|
|
15
|
+
await sendEmail({
|
|
16
|
+
to: 'oncall@acme.dev',
|
|
17
|
+
subject: `Charge failed: ${charge.id}`,
|
|
18
|
+
body: error.message,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function sendSlack(text) { /* ... */ }
|
|
23
|
+
async function sendEmail(opts) { /* ... */ }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Scheduler service — cron-style triggers.
|
|
2
|
+
import { enqueue } from './queue.mjs';
|
|
3
|
+
|
|
4
|
+
// Retry failed charges hourly
|
|
5
|
+
setInterval(async () => {
|
|
6
|
+
const failed = await db.query('SELECT id FROM charges WHERE status = $1', ['failed']);
|
|
7
|
+
for (const row of failed.rows) await enqueue({ type: 'process_charge', charge_id: row.id });
|
|
8
|
+
}, 60 * 60 * 1000);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Worker service — consumes job queue.
|
|
2
|
+
import { connect } from './queue.mjs';
|
|
3
|
+
|
|
4
|
+
const handlers = {
|
|
5
|
+
process_charge: async (job) => { /* call Stripe */ },
|
|
6
|
+
process_refund: async (job) => { /* call Stripe refund API */ },
|
|
7
|
+
settle_refund: async (job) => { /* mark refund as settled */ },
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const queue = await connect(process.env.REDIS_URL);
|
|
11
|
+
queue.consume(async (job) => {
|
|
12
|
+
const handler = handlers[job.type];
|
|
13
|
+
if (!handler) throw new Error(`Unknown job: ${job.type}`);
|
|
14
|
+
await handler(job);
|
|
15
|
+
});
|