trooth 0.1.0 → 0.2.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 +59 -11
- package/bin/trooth.mjs +189 -16
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,39 +1,87 @@
|
|
|
1
1
|
# trooth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The trust network your terminal can check. Read any company's **witnessed** trust
|
|
4
|
+
standing from the [Trooth Network](https://trooth.co). Identity, security, privacy,
|
|
5
|
+
AI practices, and more, each witnessed, signed, and dated. It also scans your own Terraform
|
|
6
|
+
plans against SOC 2, ISO 27001, GDPR, HIPAA, NIST AI RMF, and the EU AI Act.
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
**`check` reads only public, already-published records. No key, no account.**
|
|
9
|
+
**`scan` is advisory and report-only. Trooth never applies changes to your infrastructure.**
|
|
7
10
|
|
|
8
11
|
## Install / run
|
|
9
12
|
|
|
10
13
|
```bash
|
|
11
14
|
# No install needed:
|
|
12
|
-
npx trooth
|
|
15
|
+
npx trooth check stripe.com
|
|
13
16
|
|
|
14
|
-
# or
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
# or install it:
|
|
18
|
+
npm i -g trooth
|
|
19
|
+
trooth check trooth.co
|
|
17
20
|
```
|
|
18
21
|
|
|
19
22
|
## Commands
|
|
20
23
|
|
|
21
24
|
| Command | What it does |
|
|
22
25
|
| --- | --- |
|
|
26
|
+
| `trooth check <domain>` | Reads a company's witnessed standing from the live Trooth Network (`GET https://api.trooth.co/directory/api/vendors`) and prints the score, the per-discipline breakdown, live probes, attestations, and the badge / scan IDs you can verify. Returns an honest "not listed yet" when a company has no published standing. |
|
|
23
27
|
| `trooth scan <plan>` | Posts your `terraform show -json` plan to Trooth Pre-Flight (`POST https://api.trooth.co/v1/preflight`) and prints the verdict, score, and findings. Accepts a JSON file or a binary `.tfplan` (it runs `terraform show -json` for you). |
|
|
28
|
+
| `trooth eu ...` | Pushes EU AI Act evidence (datasets, evals, drift) into Trooth from CI/CD. Needs a session token or company id. |
|
|
24
29
|
| `trooth lint [path]` | Local, read-only IaC drift check. Never transmits your code. |
|
|
25
30
|
| `trooth --help` / `--version` | Help / version. |
|
|
26
31
|
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
trooth check trooth.co
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Trooth Network // witnessed · public · read-only //
|
|
40
|
+
Trooth, LLC trooth.co
|
|
41
|
+
Standing: 91/100 Listed witnessed 2026-08-30
|
|
42
|
+
|
|
43
|
+
By discipline
|
|
44
|
+
Legal ██████████ 20/20
|
|
45
|
+
Security ████████░░ 16/20
|
|
46
|
+
AI governance ██████████ 19/20
|
|
47
|
+
Business █████████░ 18/20
|
|
48
|
+
IP █████████░ 18/20
|
|
49
|
+
|
|
50
|
+
Live probes 64/65 Attestations 27/35
|
|
51
|
+
Badge bronze_rw_... Scan rw_...
|
|
52
|
+
|
|
53
|
+
A witnessed, point-in-time reading of public evidence. Not a certification.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# For scripting: raw JSON, exit code 1 when a company is not listed:
|
|
58
|
+
trooth check acme.com --json
|
|
59
|
+
```
|
|
60
|
+
|
|
27
61
|
## Flags
|
|
28
62
|
|
|
29
|
-
- `--
|
|
30
|
-
- `--
|
|
63
|
+
- `--json`: print the raw JSON record instead of the formatted view.
|
|
64
|
+
- `--strict`: scan only, exit non-zero if there are findings (default: exit 0, advisory).
|
|
31
65
|
|
|
32
66
|
## Notes
|
|
33
67
|
|
|
34
|
-
-
|
|
35
|
-
|
|
68
|
+
- `check` reads only the **public** witness directory. There is no key and no account, and
|
|
69
|
+
it never sends anything about you. It is a read.
|
|
70
|
+
- A "not listed" result is not a judgement. It means no witnessed standing has been
|
|
71
|
+
published for that domain yet. A company gets witnessed by listing at
|
|
72
|
+
<https://trooth.co/get-started>.
|
|
73
|
+
- `scan` sends only your **declared plan** to the Pre-Flight API; nothing is written back,
|
|
74
|
+
and Trooth never touches live infrastructure.
|
|
36
75
|
- Set `TROOTH_API` to point at a different base URL (defaults to `https://api.trooth.co`).
|
|
37
76
|
- Requires Node 18+ (uses built-in `fetch`).
|
|
38
77
|
|
|
78
|
+
## Use it from an AI assistant
|
|
79
|
+
|
|
80
|
+
The same public network powers Trooth's read-only MCP server, so ChatGPT, Claude, Cursor,
|
|
81
|
+
or any MCP client can ask about a company in plain words:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
https://api.trooth.co/public/mcp
|
|
85
|
+
```
|
|
86
|
+
|
|
39
87
|
Trooth automates. Trooth never signs for you.
|
package/bin/trooth.mjs
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Trooth CLI
|
|
2
|
+
// Trooth CLI. Command name: `trooth`
|
|
3
3
|
// Copyright (c) 2026 Trooth, LLC. All rights reserved.
|
|
4
4
|
//
|
|
5
5
|
// Commands:
|
|
6
|
+
// trooth check <domain> Read a company's witnessed trust standing from the
|
|
7
|
+
// public Trooth Network (read-only, no key, no account).
|
|
6
8
|
// trooth scan <plan> Send a Terraform plan to Trooth Pre-Flight (advisory, report-only).
|
|
7
9
|
// <plan> may be a `terraform show -json` JSON file, OR a binary
|
|
8
10
|
// .tfplan (the CLI will run `terraform show -json` for you).
|
|
11
|
+
// trooth eu ... Push EU AI Act evidence into Trooth from CI/CD (needs a token).
|
|
9
12
|
// trooth lint [path] Local, read-only IaC drift check (never transmits your code).
|
|
10
13
|
// trooth --help Show help. trooth --version Show version.
|
|
11
14
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
+
// `check` reads only PUBLIC, already-published witness records. `scan` is ADVISORY and
|
|
16
|
+
// REPORT-ONLY: Trooth never applies changes to your infrastructure, and the scan posts
|
|
17
|
+
// only your declared plan. Trooth automates. Trooth never signs for you.
|
|
15
18
|
|
|
16
19
|
import { readFileSync, existsSync } from 'node:fs';
|
|
17
20
|
import { execFileSync } from 'node:child_process';
|
|
18
21
|
import { createRequire } from 'node:module';
|
|
19
22
|
|
|
20
23
|
const API = process.env.TROOTH_API || 'https://api.trooth.co';
|
|
21
|
-
const J='\x1b[32m', D='\x1b[2m', B='\x1b[1m', R='\x1b[31m', A='\x1b[33m', X='\x1b[0m';
|
|
24
|
+
const J='\x1b[32m', D='\x1b[2m', B='\x1b[1m', R='\x1b[31m', A='\x1b[33m', C='\x1b[36m', X='\x1b[0m';
|
|
22
25
|
const require = createRequire(import.meta.url);
|
|
23
|
-
let VERSION = '0.
|
|
26
|
+
let VERSION = '0.2.0';
|
|
24
27
|
try { VERSION = require('../package.json').version; } catch {}
|
|
25
28
|
|
|
26
29
|
const argv = process.argv.slice(2);
|
|
@@ -28,40 +31,182 @@ const cmd = argv[0];
|
|
|
28
31
|
|
|
29
32
|
function help() {
|
|
30
33
|
console.log(`
|
|
31
|
-
${J}${B}trooth${X} ${D}v${VERSION}
|
|
34
|
+
${J}${B}trooth${X} ${D}v${VERSION} · the trust network your terminal can check${X}
|
|
32
35
|
|
|
33
36
|
${B}Usage${X}
|
|
34
|
-
trooth
|
|
37
|
+
trooth check <domain> Read a company's witnessed standing on the Trooth Network
|
|
38
|
+
trooth scan <plan> Scan a Terraform plan via Trooth Pre-Flight (advisory)
|
|
35
39
|
trooth lint [path] Local read-only IaC drift check
|
|
36
40
|
trooth --help | --version
|
|
37
41
|
|
|
38
42
|
${B}Examples${X}
|
|
43
|
+
trooth check stripe.com ${D}# read a company's witnessed record${X}
|
|
44
|
+
trooth check trooth.co --json ${D}# raw JSON, for scripting${X}
|
|
39
45
|
terraform show -json plan.tfplan > plan.json && trooth scan plan.json
|
|
40
|
-
trooth scan ./plan.tfplan ${D}# runs \`terraform show -json\` for you${X}
|
|
41
46
|
|
|
42
47
|
${B}Flags${X}
|
|
43
|
-
--
|
|
44
|
-
--
|
|
48
|
+
--json Print raw JSON instead of the formatted view
|
|
49
|
+
--strict (scan) exit non-zero if findings exist (default: advisory, exit 0)
|
|
45
50
|
|
|
46
|
-
${D}
|
|
47
|
-
Trooth automates. Trooth never signs for you.${X}
|
|
51
|
+
${D}check reads only public, already-published witness records. No key, no account.
|
|
52
|
+
scan is advisory and report-only. Trooth automates. Trooth never signs for you.${X}
|
|
48
53
|
`);
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
/* --------------------------------------------------------------- check ---- */
|
|
57
|
+
|
|
58
|
+
/** Normalize a domain the way the Trooth Network does: strip scheme, path,
|
|
59
|
+
* leading www, case, and trailing slash. */
|
|
60
|
+
function normalizeDomain(input) {
|
|
61
|
+
if (!input) return '';
|
|
62
|
+
let s = String(input).trim().toLowerCase();
|
|
63
|
+
s = s.replace(/^[a-z]+:\/\//, ''); // scheme
|
|
64
|
+
s = s.replace(/\/.*$/, ''); // path/query/hash
|
|
65
|
+
s = s.replace(/^www\./, ''); // leading www
|
|
66
|
+
s = s.replace(/\.$/, ''); // trailing dot
|
|
67
|
+
return s;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const CATEGORY_LABELS = {
|
|
71
|
+
legal: 'Legal',
|
|
72
|
+
security: 'Security',
|
|
73
|
+
ai: 'AI governance',
|
|
74
|
+
business: 'Business',
|
|
75
|
+
ip: 'IP',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Pull a 0..100 score out of the feed's score field (object or number). */
|
|
79
|
+
function scorePct(score) {
|
|
80
|
+
if (score == null) return null;
|
|
81
|
+
if (typeof score === 'number') return Math.round(score);
|
|
82
|
+
if (typeof score === 'object') {
|
|
83
|
+
if (typeof score.rate === 'number') return Math.round(score.rate * 100);
|
|
84
|
+
if (typeof score.passed === 'number' && typeof score.total === 'number' && score.total > 0)
|
|
85
|
+
return Math.round((score.passed / score.total) * 100);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function bar(passed, total) {
|
|
91
|
+
if (!total) return '';
|
|
92
|
+
const filled = Math.round((passed / total) * 10);
|
|
93
|
+
return '█'.repeat(filled) + '░'.repeat(10 - filled);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function fmtDate(iso) {
|
|
97
|
+
if (!iso) return '';
|
|
98
|
+
const d = new Date(iso);
|
|
99
|
+
if (isNaN(d)) return String(iso);
|
|
100
|
+
return d.toISOString().slice(0, 10);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function check() {
|
|
104
|
+
const domainArg = argv.find((a, i) => i > 0 && !a.startsWith('--'));
|
|
105
|
+
const asJson = argv.includes('--json');
|
|
106
|
+
const domain = normalizeDomain(domainArg);
|
|
107
|
+
if (!domain) {
|
|
108
|
+
console.error(`${R}error${X} missing <domain>. Try: trooth check stripe.com`);
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let res;
|
|
113
|
+
try {
|
|
114
|
+
res = await fetch(`${API}/directory/api/vendors`, {
|
|
115
|
+
headers: { accept: 'application/json', 'user-agent': `trooth-cli/${VERSION}` },
|
|
116
|
+
});
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error(`${R}error${X} could not reach the Trooth Network at ${API}: ${e.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
console.error(`${R}error${X} the Trooth Network returned HTTP ${res.status}.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
let data;
|
|
126
|
+
try { data = await res.json(); } catch { data = {}; }
|
|
127
|
+
const vendors = Array.isArray(data && data.vendors) ? data.vendors : [];
|
|
128
|
+
const vendor = vendors.find((v) => v && normalizeDomain(v.domain) === domain) || null;
|
|
129
|
+
|
|
130
|
+
if (asJson) {
|
|
131
|
+
console.log(JSON.stringify(vendor || { domain, listed: false }, null, 2));
|
|
132
|
+
process.exit(vendor ? 0 : 1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!vendor) {
|
|
136
|
+
console.log(`\n${B}${domain}${X} ${D}//${X} ${A}not listed on the Trooth Network yet${X}`);
|
|
137
|
+
console.log(`\n${D}No witnessed standing has been published for this domain. That is not a`);
|
|
138
|
+
console.log(`judgement. It simply has not been witnessed. A company gets witnessed by`);
|
|
139
|
+
console.log(`listing at ${X}${C}https://trooth.co/get-started${X}${D}: the scan reads its public surface and`);
|
|
140
|
+
console.log(`publishes a signed, dated standing that anyone can check.${X}\n`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const pct = scorePct(vendor.score);
|
|
145
|
+
const name = vendor.company_name || domain;
|
|
146
|
+
const tier = vendor.tier ? vendor.tier[0].toUpperCase() + vendor.tier.slice(1) : 'Listed';
|
|
147
|
+
const when = fmtDate(vendor.passed_at);
|
|
148
|
+
|
|
149
|
+
console.log(`\n${J}${B}Trooth Network${X} ${D}// witnessed · public · read-only //${X}`);
|
|
150
|
+
console.log(`${B}${name}${X} ${C}${domain}${X}`);
|
|
151
|
+
const scoreStr = pct == null ? `${D}not yet scored${X}` : `${B}${pct}/100${X}`;
|
|
152
|
+
console.log(`Standing: ${scoreStr} ${J}${tier}${X}${when ? ` ${D}witnessed ${when}${X}` : ''}`);
|
|
153
|
+
|
|
154
|
+
// Categories (object keyed by discipline).
|
|
155
|
+
const cats = vendor.categories;
|
|
156
|
+
if (cats && typeof cats === 'object' && !Array.isArray(cats)) {
|
|
157
|
+
console.log(`\n${B}By discipline${X}`);
|
|
158
|
+
for (const key of ['legal', 'security', 'ai', 'business', 'ip']) {
|
|
159
|
+
const c = cats[key];
|
|
160
|
+
if (!c || typeof c.total !== 'number') continue;
|
|
161
|
+
const label = (CATEGORY_LABELS[key] || key).padEnd(14);
|
|
162
|
+
const r = c.total > 0 ? c.passed / c.total : 0;
|
|
163
|
+
const col = r >= 0.9 ? J : r >= 0.5 ? A : R;
|
|
164
|
+
console.log(` ${label} ${col}${bar(c.passed, c.total)}${X} ${String(c.passed).padStart(2)}/${c.total}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const line = [];
|
|
169
|
+
if (vendor.probes && typeof vendor.probes.total === 'number')
|
|
170
|
+
line.push(`${B}Live probes${X} ${vendor.probes.passed}/${vendor.probes.total}`);
|
|
171
|
+
if (vendor.attested && typeof vendor.attested.total === 'number')
|
|
172
|
+
line.push(`${B}Attestations${X} ${vendor.attested.passed}/${vendor.attested.total}`);
|
|
173
|
+
if (line.length) console.log(`\n${line.join(' ')}`);
|
|
174
|
+
|
|
175
|
+
const ids = [];
|
|
176
|
+
if (vendor.badge_id) ids.push(`${D}Badge ${vendor.badge_id}${X}`);
|
|
177
|
+
if (vendor.scan_id) ids.push(`${D}Scan ${vendor.scan_id}${X}`);
|
|
178
|
+
if (ids.length) console.log(ids.join(' '));
|
|
179
|
+
|
|
180
|
+
const events = Array.isArray(vendor.events) ? vendor.events : [];
|
|
181
|
+
if (events.length) {
|
|
182
|
+
console.log(`\n${B}Recent witness events${X}`);
|
|
183
|
+
for (const e of events.slice(0, 3)) {
|
|
184
|
+
if (!e || !e.type) continue;
|
|
185
|
+
console.log(` ${J}•${X} ${D}${fmtDate(e.at)}${X} ${e.type.replace(/_/g, ' ')}${e.detail ? `${D} ${e.detail}${X}` : ''}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(`\n${D}A witnessed, point-in-time reading of public evidence. Not a certification.`);
|
|
190
|
+
console.log(`Full record and directory: ${X}${C}https://trooth.co/network${X}${D} · Signing keys: ${API}/public/keys${X}\n`);
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* ---------------------------------------------------------------- scan ---- */
|
|
195
|
+
|
|
51
196
|
function loadPlan(file) {
|
|
52
197
|
if (!file) { console.error(`${R}error${X} missing <plan> argument. Try: trooth scan ./plan.json`); process.exit(2); }
|
|
53
198
|
if (!existsSync(file)) { console.error(`${R}error${X} file not found: ${file}`); process.exit(2); }
|
|
54
199
|
// Try to read as JSON (a `terraform show -json` document).
|
|
55
200
|
let raw = '';
|
|
56
201
|
try { raw = readFileSync(file, 'utf8'); } catch (e) { console.error(`${R}error${X} cannot read ${file}: ${e.message}`); process.exit(2); }
|
|
57
|
-
try { return JSON.parse(raw); } catch { /* not JSON
|
|
202
|
+
try { return JSON.parse(raw); } catch { /* not JSON, likely a binary .tfplan */ }
|
|
58
203
|
// Fall back: ask Terraform to render the plan as JSON.
|
|
59
204
|
try {
|
|
60
205
|
const out = execFileSync('terraform', ['show', '-json', file], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
61
206
|
return JSON.parse(out);
|
|
62
207
|
} catch (e) {
|
|
63
208
|
console.error(`${R}error${X} ${file} is not JSON, and \`terraform show -json\` failed.`);
|
|
64
|
-
console.error(`${D} Generate JSON first: terraform show -json ${file} > plan.json
|
|
209
|
+
console.error(`${D} Generate JSON first: terraform show -json ${file} > plan.json then trooth scan plan.json${X}`);
|
|
65
210
|
process.exit(2);
|
|
66
211
|
}
|
|
67
212
|
}
|
|
@@ -92,7 +237,7 @@ async function scan() {
|
|
|
92
237
|
|
|
93
238
|
if (asJson) { console.log(JSON.stringify(delta, null, 2)); process.exit(strict && (delta.summary?.fail > 0) ? 1 : 0); }
|
|
94
239
|
|
|
95
|
-
const score = delta.score ?? '
|
|
240
|
+
const score = delta.score ?? '-';
|
|
96
241
|
const verdict = (delta.verdict || 'reviewed').toUpperCase();
|
|
97
242
|
const s = delta.summary || {};
|
|
98
243
|
console.log(`\n${J}${B}Trooth Pre-Flight${X} ${D}// advisory · report-only //${X}`);
|
|
@@ -116,6 +261,32 @@ async function scan() {
|
|
|
116
261
|
process.exit(strict && (s.fail > 0) ? 1 : 0); // advisory by default: exit 0
|
|
117
262
|
}
|
|
118
263
|
|
|
264
|
+
/* ------------------------------------------------------------------ eu ---- */
|
|
265
|
+
// `trooth eu` pushes EU AI Act evidence into Trooth from CI/CD (automated observability).
|
|
266
|
+
// trooth eu --token <t> --datasets ds.json --evals ev.json --drift dr.json
|
|
267
|
+
// Each file is a JSON array. Auth: TROOTH_TOKEN env or --token (bearer or x-trooth-company).
|
|
268
|
+
async function eu() {
|
|
269
|
+
const flag = (name) => { const i = argv.indexOf(name); return i >= 0 ? argv[i + 1] : null; };
|
|
270
|
+
const token = flag('--token') || process.env.TROOTH_TOKEN || '';
|
|
271
|
+
const company = flag('--company') || process.env.TROOTH_COMPANY || '';
|
|
272
|
+
if (!token && !company) { console.error(`${R}error${X} provide --token <session> or --company <id> (or TROOTH_TOKEN / TROOTH_COMPANY env).`); process.exit(2); }
|
|
273
|
+
const load = (f) => { if (!f) return undefined; try { return JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error(`${R}error${X} cannot read ${f}: ${e.message}`); process.exit(2); } };
|
|
274
|
+
const body = { datasets: load(flag('--datasets')), evals: load(flag('--evals')), drift: load(flag('--drift')) };
|
|
275
|
+
if (!body.datasets && !body.evals && !body.drift) { console.error(`${R}error${X} provide at least one of --datasets/--evals/--drift <file.json>`); process.exit(2); }
|
|
276
|
+
const headers = { 'content-type': 'application/json', 'user-agent': `trooth-cli/${VERSION}` };
|
|
277
|
+
if (token) headers['authorization'] = `Bearer ${token}`;
|
|
278
|
+
if (company) headers['x-trooth-company'] = company;
|
|
279
|
+
let res;
|
|
280
|
+
try { res = await fetch(`${API}/v1/eu/ingest`, { method: 'POST', headers, body: JSON.stringify(body) }); }
|
|
281
|
+
catch (e) { console.error(`${R}error${X} could not reach Trooth at ${API}: ${e.message}`); process.exit(1); }
|
|
282
|
+
if (!res.ok) { console.error(`${R}error${X} HTTP ${res.status}. ${(await res.text().catch(()=> '')).slice(0,300)}`); process.exit(1); }
|
|
283
|
+
const out = await res.json();
|
|
284
|
+
console.log(`\n${J}${B}Trooth EU AI Act${X} ${D}// evidence ingested //${X}`);
|
|
285
|
+
console.log(`datasets ${B}${out.ingested?.datasets ?? 0}${X} · evals ${B}${out.ingested?.evals ?? 0}${X} · drift ${B}${out.ingested?.drift ?? 0}${X}`);
|
|
286
|
+
console.log(`\n${D}${out.disclaimer || 'Advisory. Trooth automates, computes, and documents; it never signs.'}${X}\n`);
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}
|
|
289
|
+
|
|
119
290
|
async function lint() {
|
|
120
291
|
// Local, read-only .tf drift check. Reuses the published @trooth/os linter if present,
|
|
121
292
|
// otherwise runs a built-in minimal ruleset. Never transmits your code.
|
|
@@ -128,7 +299,9 @@ async function lint() {
|
|
|
128
299
|
(async () => {
|
|
129
300
|
if (cmd === '--version' || cmd === '-v') { console.log(VERSION); return; }
|
|
130
301
|
if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') { help(); return; }
|
|
302
|
+
if (cmd === 'check') return check();
|
|
131
303
|
if (cmd === 'scan') return scan();
|
|
304
|
+
if (cmd === 'eu') return eu();
|
|
132
305
|
if (cmd === 'lint') return lint();
|
|
133
306
|
console.error(`${R}error${X} unknown command: ${cmd}\n`); help(); process.exit(2);
|
|
134
307
|
})();
|
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trooth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Trooth CLI
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Trooth CLI. Check any company's witnessed trust standing from your terminal (read-only, no key), and scan a Terraform plan against SOC 2 / ISO 27001 / GDPR / HIPAA / NIST AI RMF / EU AI Act. Advisory and report-only. Trooth never applies changes.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin":
|
|
7
|
-
"trooth": "./bin/trooth.mjs"
|
|
8
|
-
},
|
|
6
|
+
"bin": "bin/trooth.mjs",
|
|
9
7
|
"files": [
|
|
10
8
|
"bin/",
|
|
11
9
|
"README.md"
|
|
@@ -14,6 +12,7 @@
|
|
|
14
12
|
"node": ">=18"
|
|
15
13
|
},
|
|
16
14
|
"scripts": {
|
|
15
|
+
"check": "node ./bin/trooth.mjs check",
|
|
17
16
|
"scan": "node ./bin/trooth.mjs scan"
|
|
18
17
|
},
|
|
19
18
|
"license": "MIT",
|
|
@@ -24,6 +23,13 @@
|
|
|
24
23
|
"url": "https://github.com/trooth/trooth-cli.git"
|
|
25
24
|
},
|
|
26
25
|
"keywords": [
|
|
26
|
+
"trust",
|
|
27
|
+
"vendor-trust",
|
|
28
|
+
"trust-network",
|
|
29
|
+
"company-check",
|
|
30
|
+
"due-diligence",
|
|
31
|
+
"mcp",
|
|
32
|
+
"ai-agents",
|
|
27
33
|
"compliance",
|
|
28
34
|
"soc2",
|
|
29
35
|
"iso27001",
|