verity-framework 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 CHANGED
@@ -6,25 +6,89 @@ Verity is a CI/CD-native, GitHub-native, production-lifecycle AI software delive
6
6
 
7
7
  Most AI coding tools stop when the code is written. Verity keeps going until the software is tested, deployed, and **proven working in front of a user**. It runs a project as a sequence of specialized AI roles (architect, builder, reviewer, release operator, verifier…) that hand work off through clear contracts, with GitHub as the source of truth.
8
8
 
9
- > **Status: design complete, pre-implementation.** This repo currently tracks the design. Implementation begins with Verity's own *walking skeleton* see [`docs/walking-skeleton-plan.md`](docs/walking-skeleton-plan.md).
9
+ > **Status:** published [`verity-framework@0.2.0`](https://www.npmjs.com/package/verity-framework) on npm. 13 role commands, the full Relay/Ledger/Gate/Shipyard/Verify engine, adapters for Claude Code + OpenCode, and the deployment-methods catalog (see below).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i -g verity-framework
15
+ verity install --claude # or: --opencode
16
+ ```
17
+
18
+ **Prerequisites:** a GitHub account · Node ≥16 · `git` and the GitHub CLI (`gh`) installed **and signed in** (`gh auth login` — installing `gh` is not the same as being authenticated). Preflight:
19
+
20
+ ```bash
21
+ node -v && git --version && gh auth status
22
+ ```
23
+
24
+ Then, in your AI assistant, start a project with `/verity:vision`.
25
+
26
+ ## Deployment methods
27
+
28
+ **What it is.** A catalog of the places *you* can deploy to (an AWS box, a server on your LAN, a managed host…), plus a per-app record of where each project actually runs. Verity reads it when the **Architect** designs an app, so deployment becomes a *deliberate choice you make* — not an accident.
29
+
30
+ **Why it exists.** Without it, an AI agent picks a host by whatever happens to be lying around — e.g. suggesting Render.com simply because a Render tool is installed in its environment. That's not a decision; it's a coincidence. The catalog makes the Architect ask *"here's what you can deploy to — which one for this app?"*, and record the answer as an [ADR](docs/commands.md). It also keeps host and credential details **out of git** while still letting a team share them safely.
31
+
32
+ **Two files, two scopes:**
33
+
34
+ | | Global catalog | Per-app access |
35
+ |---|---|---|
36
+ | **Path** | `~/.verity/deployment-methods.md` | `.verity/deploy-access.md` |
37
+ | **Scope** | You, across *every* project | One app |
38
+ | **Holds** | The deploy targets you *can* use | How to reach *this* app's host |
39
+ | **Created** | At `verity install` (seeded once, never overwritten) | Written by the Architect, per app |
40
+ | **In git?** | No — lives in your home dir | **No — gitignored.** A committed, secret-free pointer (`.verity/deploy-access.README.md`) tells teammates who lack it to ask the project admin |
41
+
42
+ > 🔒 **Both files reference credential *locations*, never secrets.** Point at a key file (`~/.ssh/prod.pem`), an SSO profile, or a secret-store entry — never paste an actual key, password, or token. The real per-app file is shared out-of-band (not through the repo), so nothing sensitive ever lands in git history. (Same rule as `STATUS.md`.)
43
+
44
+ **How to use it.** The global catalog is seeded at install with two worked examples (AWS EC2 over SSH, and a local-network server) — edit it to describe *your* real targets:
45
+
46
+ ```bash
47
+ verity deployment list # what targets you have (the Architect reads this)
48
+ verity deployment show aws-ec2 # one method's details
49
+ verity deployment path # where the catalog file lives
50
+ verity deployment edit # open it in $EDITOR to add/adjust targets
51
+ ```
52
+
53
+ When the Architect designs an app it runs `verity deployment list`, helps you **pick a target** (recording it as an ADR), and — if nothing real is configured yet — **asks how you want to deploy and offers suggestions**. It then sets up the per-app access file:
54
+
55
+ ```bash
56
+ verity deployment init-access # gitignore the real file + commit the "ask the admin" pointer
57
+ verity deployment access # is the access file present on this machine? if not, who to ask
58
+ ```
59
+
60
+ …and writes `.verity/deploy-access.md` with how to reach this app's host. The **Release/Deploy Operator** (`/verity:ship`) consumes that target when it builds the deploy step.
61
+
62
+ ## Guides (interactive, beginner-friendly)
63
+
64
+ Self-contained HTML — clone/download the repo and open them in any browser (no server or internet needed):
65
+
66
+ - [`docs/verity-overview.html`](docs/verity-overview.html) — **Overview**: what Verity is, the mental model, how it works (no jargon assumed)
67
+ - [`docs/verity-usage.html`](docs/verity-usage.html) — **Usage**: install + command-by-command recipes + pro tips (Claude Code / OpenCode toggle)
68
+ - [`docs/verity-flows.html`](docs/verity-flows.html) — **Flows**: start-from-scratch vs add-to-an-existing-project, side by side
69
+ - [`docs/verity-flows.drawio`](docs/verity-flows.drawio) — the flow diagram as an editable draw.io / diagrams.net file
70
+ - [`docs/commands.md`](docs/commands.md) — **Command reference**: all 13 `/verity:*` role commands and what each one does
71
+ - [`docs/explainer-kit.md`](docs/explainer-kit.md) — **Explainer kit**: a briefing for an AI to describe Verity to humans (podcast/deck/talk) — story, diagrams, soundbites, fact sheet
10
72
 
11
73
  ## Subsystems
12
74
  - **Relay** — role orchestration + the stream loop + dependency engine
13
- - **Shipyard** — CI/CD spine + Release/Deploy Operator + runtime truth (`STATUS.md`)
75
+ - **Shipyard** — CI/CD spine + Release/Deploy Operator + deployment-methods catalog + runtime truth (`STATUS.md`)
14
76
  - **Ledger** — GitHub-derived state engine (no stale files)
15
77
  - **Gate** — review + security + the quality gates
16
78
  - **Verify** — live "observably-works" verification
17
79
 
18
- ## Docs
80
+ ## Design docs
19
81
  - [`docs/framework-spec.md`](docs/framework-spec.md) — the build-ready architecture spec
20
- - [`docs/roles-spec.md`](docs/roles-spec.md) — working log + full rationale (all 14 roles)
82
+ - [`docs/roles-spec.md`](docs/roles-spec.md) — working log + full rationale (all roles)
21
83
  - [`docs/interview-findings.md`](docs/interview-findings.md) — forensic interview of the real build that drove the design
22
84
  - [`docs/brand.md`](docs/brand.md) — naming / positioning
23
- - [`docs/features/helper-bot.md`](docs/features/helper-bot.md) — drop-in feature #1
24
85
  - [`docs/walking-skeleton-plan.md`](docs/walking-skeleton-plan.md) — the first implementation slice
25
86
 
26
87
  ## Package
27
88
  `verity-framework` (npm) · CLI binary: `verity` · Node ≥16 · host deps: `git`, `gh`
28
89
 
90
+ ## Contributing
91
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) — local setup, the test/lint checks, project layout, and conventions.
92
+
29
93
  ## License
30
94
  MIT
@@ -34,24 +34,44 @@ and the walking-skeleton definition handed to /verity:plan.
34
34
  ```
35
35
  Then fill in Context / Decision / Alternatives considered / Consequences.
36
36
 
37
- 3. **Freeze the core contracts** (wire/JWT/schema between components) additive-only
37
+ 3. **Choose the deployment target.** Read the user's global catalog of where they can
38
+ deploy, and pick a target for THIS app — never assume one (don't reach for a host
39
+ just because its MCP/CLI happens to be installed):
40
+ ```bash
41
+ verity deployment list # ~/.verity/deployment-methods.md (locations, not secrets)
42
+ ```
43
+ - Work with the user to choose a method; record the choice as an ADR.
44
+ - If `hasConfigured` is false (only the shipped examples remain), ASK how they want
45
+ to deploy and offer suggestions (managed PaaS, a VM over SSH, a local/LAN server…),
46
+ then help them add it: `verity deployment edit`.
47
+ - Set up the per-app access file (committed pointer + gitignore — no secrets in git):
48
+ ```bash
49
+ verity deployment init-access
50
+ ```
51
+ Then WRITE `.verity/deploy-access.md` with how to reach this app's host —
52
+ credential **locations** only (key files, SSO profiles, secret-store entries),
53
+ never raw secrets. That file is gitignored and shared out-of-band; teammates who
54
+ lack it are pointed at the project admin. The Release/Deploy Operator (`/verity:ship`)
55
+ consumes this target when it builds `deploy.sh`.
56
+
57
+ 4. **Freeze the core contracts** (wire/JWT/schema between components) — additive-only
38
58
  thereafter; a breaking change is a NEW contract, never an edit:
39
59
  ```bash
40
60
  verity contract new <seam-name>
41
61
  ```
42
62
 
43
- 4. Offer the **drop-in feature catalog**. For each feature the user wants, note that
63
+ 5. Offer the **drop-in feature catalog**. For each feature the user wants, note that
44
64
  its stages will fold into the plan:
45
65
  ```bash
46
66
  verity feature list
47
67
  verity feature show <id>
48
68
  ```
49
69
 
50
- 5. Define the **walking skeleton** (Stage 0): the thinnest end-to-end slice that
70
+ 6. Define the **walking skeleton** (Stage 0): the thinnest end-to-end slice that
51
71
  compiles, runs, passes one real test, goes green in CI, and deploys. This blocks
52
72
  all feature stages and proves the spine.
53
73
 
54
- 6. Hand off to **/verity:plan** (Intake/Planner) to decompose the design + accepted
74
+ 7. Hand off to **/verity:plan** (Intake/Planner) to decompose the design + accepted
55
75
  features into the initial thin backlog of stages.
56
76
 
57
77
  Runtime note: if `verity` is not on PATH, use `node "$HOME/.claude/verity/bin/verity.cjs" ...`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verity-framework",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Verity — a CI/CD-native, GitHub-native, production-lifecycle AI software delivery framework.",
5
5
  "keywords": ["verity", "ai", "ci-cd", "github", "devops", "agent", "framework"],
6
6
  "author": "Sean Mahoney",
@@ -0,0 +1,230 @@
1
+ // Verity deployment methods (framework-spec.md — deployment-target feature).
2
+ //
3
+ // TWO scopes, deliberately separate:
4
+ //
5
+ // 1. GLOBAL catalog — `~/.verity/deployment-methods.md`. Your private inventory
6
+ // of where you CAN deploy (one per machine/user, reused across every project).
7
+ // Seeded at `verity install`, edited with `verity deployment edit`, read by the
8
+ // Architect with `verity deployment list`. Holds credential LOCATIONS, never
9
+ // secret values. NOT in any repo.
10
+ //
11
+ // 2. PER-PROJECT access file — `.verity/deploy-access.md`. Written by the Architect
12
+ // for ONE app: how to reach THAT app's host. Gitignored + shared out-of-band; a
13
+ // secret-free pointer (`.verity/deploy-access.README.md`) IS committed so a
14
+ // teammate without the file is told to get it from the project admin.
15
+ const fs = require('node:fs');
16
+ const os = require('node:os');
17
+ const path = require('node:path');
18
+ const { spawnSync } = require('node:child_process');
19
+
20
+ const identity = require('./identity.cjs');
21
+ const { render } = require('./core.cjs');
22
+
23
+ const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates');
24
+
25
+ const ACCESS_FILE = '.verity/deploy-access.md'; // gitignored real file
26
+ const ACCESS_POINTER = '.verity/deploy-access.README.md'; // committed pointer
27
+
28
+ function readTemplate(name) {
29
+ return fs.readFileSync(path.join(TEMPLATES_DIR, name), 'utf8');
30
+ }
31
+
32
+ // --- GLOBAL catalog -------------------------------------------------------
33
+
34
+ // User-global, harness-independent, and OUTSIDE the install dir (which `verity
35
+ // install` clobbers on every reinstall). VERITY_HOME / opts.home override for tests.
36
+ function homeDir(opts = {}) {
37
+ return opts.home || process.env.VERITY_HOME || path.join(os.homedir(), '.verity');
38
+ }
39
+
40
+ function globalPath(opts = {}) {
41
+ return path.join(homeDir(opts), 'deployment-methods.md');
42
+ }
43
+
44
+ // Seed the catalog only if absent — NEVER clobber the user's edits.
45
+ function ensure(opts = {}) {
46
+ const p = globalPath(opts);
47
+ if (fs.existsSync(p)) {
48
+ return { created: false, path: p };
49
+ }
50
+ fs.mkdirSync(path.dirname(p), { recursive: true });
51
+ fs.writeFileSync(p, readTemplate('deployment-methods.md.tmpl'));
52
+ return { created: true, path: p };
53
+ }
54
+
55
+ // Each method is a `## <id> — <Title>` section; `- **status:** <x>` tags it.
56
+ // status defaults to `active` when a user adds a method without one; the shipped
57
+ // samples are `example` so the Architect can tell "real option" from "placeholder".
58
+ function parseMethods(text) {
59
+ const methods = [];
60
+ let cur = null;
61
+ for (const line of String(text).split('\n')) {
62
+ const head = line.match(/^##\s+([a-z0-9][a-z0-9-]*)\s+—\s+(.+)$/);
63
+ if (head) {
64
+ if (cur) {
65
+ methods.push(cur);
66
+ }
67
+ cur = { id: head[1], title: head[2].trim(), status: 'active', content: line };
68
+ continue;
69
+ }
70
+ if (cur) {
71
+ cur.content += `\n${line}`;
72
+ const st = line.match(/^-\s+\*\*status:\*\*\s*(.+)$/i);
73
+ if (st) {
74
+ cur.status = st[1].trim().toLowerCase();
75
+ }
76
+ }
77
+ }
78
+ if (cur) {
79
+ methods.push(cur);
80
+ }
81
+ return methods;
82
+ }
83
+
84
+ function list(opts = {}) {
85
+ const { path: p } = ensure(opts);
86
+ const methods = parseMethods(fs.readFileSync(p, 'utf8')).map((m) => ({
87
+ id: m.id,
88
+ title: m.title,
89
+ status: m.status,
90
+ }));
91
+ const configured = methods.filter((m) => m.status !== 'example');
92
+ return { path: p, methods, configured, hasConfigured: configured.length > 0 };
93
+ }
94
+
95
+ function show(id, opts = {}) {
96
+ if (!id) {
97
+ throw new Error('show requires a method id');
98
+ }
99
+ const { path: p } = ensure(opts);
100
+ const method = parseMethods(fs.readFileSync(p, 'utf8')).find((m) => m.id === id);
101
+ if (!method) {
102
+ throw new Error(`no such deployment method: ${id}`);
103
+ }
104
+ return method;
105
+ }
106
+
107
+ // Open the catalog in $EDITOR when run interactively; otherwise just report the
108
+ // path (the agent edits via its own Write tool, not an interactive editor).
109
+ function edit(opts = {}) {
110
+ const { path: p } = ensure(opts);
111
+ const editor = opts.editor || process.env.VISUAL || process.env.EDITOR || 'nano';
112
+ const isTTY = opts.isTTY !== undefined ? opts.isTTY : Boolean(process.stdout.isTTY);
113
+ if (!isTTY) {
114
+ return { path: p, editor, opened: false, hint: `open it: ${editor} ${p}` };
115
+ }
116
+ const run = opts.spawn || ((cmd, a) => spawnSync(cmd, a, { stdio: 'inherit' }));
117
+ run(editor, [p]);
118
+ return { path: p, editor, opened: true };
119
+ }
120
+
121
+ // --- PER-PROJECT access ---------------------------------------------------
122
+
123
+ function adminFor(cwd) {
124
+ try {
125
+ const { manifest } = identity.get(cwd);
126
+ return manifest.owner || manifest.name || manifest.slug || '(project admin)';
127
+ } catch (_e) {
128
+ return '(project admin — lock identity first)';
129
+ }
130
+ }
131
+
132
+ // Add a line to the project .gitignore if not already present. Returns whether the
133
+ // file changed (idempotent).
134
+ function ensureIgnored(cwd, line) {
135
+ const gi = path.join(cwd, '.gitignore');
136
+ const text = fs.existsSync(gi) ? fs.readFileSync(gi, 'utf8') : '';
137
+ if (text.split('\n').some((l) => l.trim() === line)) {
138
+ return false;
139
+ }
140
+ const prefix = text.length && !text.endsWith('\n') ? '\n' : '';
141
+ fs.writeFileSync(gi, `${text}${prefix}${line}\n`);
142
+ return true;
143
+ }
144
+
145
+ // Set up the committed pointer + gitignore for a project's access file. Does NOT
146
+ // write the real access file — the Architect writes that with real host detail.
147
+ function initAccess(cwd) {
148
+ fs.mkdirSync(path.join(cwd, '.verity'), { recursive: true });
149
+ const admin = adminFor(cwd);
150
+ const ignored = ensureIgnored(cwd, ACCESS_FILE);
151
+
152
+ const pointer = path.join(cwd, ACCESS_POINTER);
153
+ let pointerCreated = false;
154
+ if (!fs.existsSync(pointer)) {
155
+ fs.writeFileSync(pointer, render(readTemplate('deploy-access-pointer.md.tmpl'), { admin }));
156
+ pointerCreated = true;
157
+ }
158
+
159
+ return {
160
+ accessFile: ACCESS_FILE,
161
+ accessPath: path.join(cwd, ACCESS_FILE),
162
+ pointer: ACCESS_POINTER,
163
+ pointerCreated,
164
+ gitignoreUpdated: ignored,
165
+ admin,
166
+ next: `Write ${ACCESS_FILE} with how to reach this app's host — credential LOCATIONS only.`,
167
+ };
168
+ }
169
+
170
+ // Report whether THIS machine has the (gitignored) access file; if not, name the
171
+ // admin to request it from.
172
+ function accessStatus(cwd) {
173
+ const present = fs.existsSync(path.join(cwd, ACCESS_FILE));
174
+ const admin = adminFor(cwd);
175
+ return {
176
+ present,
177
+ accessFile: ACCESS_FILE,
178
+ pointerPresent: fs.existsSync(path.join(cwd, ACCESS_POINTER)),
179
+ admin,
180
+ message: present
181
+ ? `${ACCESS_FILE} is present.`
182
+ : `${ACCESS_FILE} not found — request it from the project admin: ${admin}.`,
183
+ };
184
+ }
185
+
186
+ function dispatch(args, flags) {
187
+ const verb = args[0];
188
+ const cwd = flags.cwd || process.cwd();
189
+ const opts = { home: flags.home };
190
+ if (verb === 'list') {
191
+ return list(opts);
192
+ }
193
+ if (verb === 'show') {
194
+ return show(args[1], opts);
195
+ }
196
+ if (verb === 'path') {
197
+ const p = globalPath(opts);
198
+ return { path: p, raw: p };
199
+ }
200
+ if (verb === 'ensure') {
201
+ return ensure(opts);
202
+ }
203
+ if (verb === 'edit') {
204
+ return edit(opts);
205
+ }
206
+ if (verb === 'init-access') {
207
+ return initAccess(cwd);
208
+ }
209
+ if (verb === 'access') {
210
+ return accessStatus(cwd);
211
+ }
212
+ throw new Error(
213
+ `unknown deployment verb: ${verb || '(none)'} — use list|show|path|edit|ensure|init-access|access`,
214
+ );
215
+ }
216
+
217
+ module.exports = {
218
+ homeDir,
219
+ globalPath,
220
+ ensure,
221
+ parseMethods,
222
+ list,
223
+ show,
224
+ edit,
225
+ initAccess,
226
+ accessStatus,
227
+ dispatch,
228
+ ACCESS_FILE,
229
+ ACCESS_POINTER,
230
+ };
@@ -7,8 +7,17 @@ const fs = require('node:fs');
7
7
  const os = require('node:os');
8
8
  const path = require('node:path');
9
9
 
10
+ const deployment = require('./deployment.cjs');
11
+
10
12
  const PKG_ROOT = path.join(__dirname, '..', '..', '..');
11
13
 
14
+ // Part of setup: seed the user-global deployment-methods catalog (NEVER clobbered).
15
+ // It lives in the user's home (~/.verity), independent of the harness target dir.
16
+ function seedDeploymentMethods(opts) {
17
+ const seed = deployment.ensure({ home: opts.home });
18
+ return { ...seed, label: `${seed.path}${seed.created ? '' : ' (existing)'}` };
19
+ }
20
+
12
21
  function commandFiles(srcCommands) {
13
22
  return fs.readdirSync(srcCommands).filter((n) => n.endsWith('.md'));
14
23
  }
@@ -38,7 +47,11 @@ function installClaude(opts = {}) {
38
47
  copyInternals(target);
39
48
  installed.push('verity/');
40
49
 
41
- return { harness: 'claude', target, installed };
50
+ // 3. Seed the global deployment-methods catalog (setup step).
51
+ const deploymentMethods = seedDeploymentMethods(opts);
52
+ installed.push(deploymentMethods.label);
53
+
54
+ return { harness: 'claude', target, installed, deploymentMethods };
42
55
  }
43
56
 
44
57
  // --- OpenCode adapter ---
@@ -85,17 +98,20 @@ function installOpenCode(opts = {}) {
85
98
  copyInternals(target);
86
99
  installed.push('verity/');
87
100
 
88
- return { harness: 'opencode', target, installed };
101
+ const deploymentMethods = seedDeploymentMethods(opts);
102
+ installed.push(deploymentMethods.label);
103
+
104
+ return { harness: 'opencode', target, installed, deploymentMethods };
89
105
  }
90
106
 
91
107
  function dispatch(_args, flags) {
92
108
  if (flags.opencode) {
93
- return installOpenCode({ target: flags.target });
109
+ return installOpenCode({ target: flags.target, home: flags.home });
94
110
  }
95
111
  if (flags.codex || flags.gemini) {
96
112
  throw new Error('only the claude and opencode adapters are implemented so far');
97
113
  }
98
- return installClaude({ target: flags.target });
114
+ return installClaude({ target: flags.target, home: flags.home });
99
115
  }
100
116
 
101
117
  module.exports = {
@@ -26,6 +26,7 @@ const map = require('./lib/map.cjs');
26
26
  const recovery = require('./lib/recovery.cjs');
27
27
  const golive = require('./lib/golive.cjs');
28
28
  const smoke = require('./lib/smoke.cjs');
29
+ const deployment = require('./lib/deployment.cjs');
29
30
 
30
31
  function parseArgs(argv) {
31
32
  const positional = [];
@@ -154,6 +155,9 @@ const COMMANDS = {
154
155
  smoke(rest, flags) {
155
156
  return smoke.dispatch(rest, flags);
156
157
  },
158
+ deployment(rest, flags) {
159
+ return deployment.dispatch(rest, flags);
160
+ },
157
161
  };
158
162
 
159
163
  function main() {
@@ -0,0 +1,13 @@
1
+ # Deployment access — obtain from the project admin
2
+
3
+ This project deploys to a specific host. The access details live in
4
+ `.verity/deploy-access.md`, which is **intentionally gitignored** — it is never
5
+ committed, so no host or credential detail ever lands in git history.
6
+
7
+ **If you do not have `.verity/deploy-access.md` on your machine, request it from the
8
+ project admin: {{admin}}.** They maintain it and share it out-of-band (not through
9
+ this repo).
10
+
11
+ Maintainers: write or update the real file with `/verity:architect`, or by hand.
12
+ Keep it to credential *locations* (key files, SSO profiles, secret-store entries) —
13
+ never raw secrets.
@@ -0,0 +1,32 @@
1
+ # Your Verity deployment methods
2
+
3
+ > **Global to YOU, across every project.** This is your private inventory of the
4
+ > places you *can* deploy to. The Architect (`/verity:architect`) reads it with
5
+ > `verity deployment list` and works with you to choose a target for each app.
6
+ >
7
+ > ⚠️ **Reference credential LOCATIONS, never paste secrets.** Point at a key file,
8
+ > an SSO profile, or a secret-store entry — never an actual key, password, or token.
9
+ > This file lives at `~/.verity/deployment-methods.md`; it is NOT in any repo, but
10
+ > treat it as sensitive anyway.
11
+ >
12
+ > Edit it any time with `verity deployment edit` (or open the path printed by
13
+ > `verity deployment path`). Add one `## <id> — <Title>` section per method, and set
14
+ > `- **status:** active` once it is real (the two below are `example` — replace or
15
+ > delete them).
16
+
17
+ ## aws-ec2 — AWS EC2 over SSH (example — edit or delete)
18
+ - **status:** example
19
+ - **provider:** AWS
20
+ - **host:** `<ec2-public-dns-or-elastic-ip>`
21
+ - **user:** `ubuntu`
22
+ - **access:** `ssh -i ~/.ssh/<your-key>.pem ubuntu@<host>` — key *location* only
23
+ - **notes:** region `<e.g. us-east-1>`; the security group must allow your IP on
24
+ ports 22 (SSH) and 443 (HTTPS).
25
+
26
+ ## local-server — Local network server (example — edit or delete)
27
+ - **status:** example
28
+ - **provider:** self-hosted (LAN)
29
+ - **host:** `<hostname-or-10.0.0.x>`
30
+ - **user:** `<you>`
31
+ - **access:** `ssh <user>@<host>` over the local network / VPN
32
+ - **notes:** only reachable on the LAN or while connected to the VPN — not public.
@@ -7,3 +7,6 @@ dist/
7
7
  !.env.*.example
8
8
  # Verity's derived state cache — never authoritative (framework-spec §5)
9
9
  .verity-cache/
10
+ # Per-app deployment access (host + credential locations) — shared out-of-band,
11
+ # never committed. The committed pointer (.verity/deploy-access.README.md) stays.
12
+ .verity/deploy-access.md