aiwg 2026.7.12 → 2026.7.13

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.
Files changed (35) hide show
  1. package/README.md +3 -0
  2. package/agentic/code/frameworks/research-complete/lint/citation-resolves.yaml +2 -3
  3. package/agentic/code/frameworks/research-complete/skills/research-lint/SKILL.md +10 -5
  4. package/agentic/code/frameworks/sdlc-complete/templates/openhuman/AGENTS.md.aiwg-template +10 -9
  5. package/dist/src/lint/runner.js +62 -20
  6. package/docs/cli-reference.md +2 -1
  7. package/docs/contributing/ci-cd-secrets.md +81 -168
  8. package/docs/contributing/secret-rotation.md +24 -49
  9. package/docs/contributing/versioning.md +33 -32
  10. package/docs/customization/user-scope-deployment.md +4 -3
  11. package/docs/getting-started/audit-existing-code.md +6 -0
  12. package/docs/getting-started/just-try-it.md +14 -2
  13. package/docs/how-it-works.md +2 -2
  14. package/docs/integrations/claude-code-quickstart.md +5 -11
  15. package/docs/integrations/codex-quickstart.md +3 -3
  16. package/docs/integrations/copilot-quickstart.md +3 -3
  17. package/docs/integrations/cursor-quickstart.md +3 -3
  18. package/docs/integrations/factory-quickstart.md +3 -3
  19. package/docs/integrations/openhuman-quickstart.md +6 -1
  20. package/docs/integrations/warp-terminal-quickstart.md +3 -3
  21. package/docs/integrations/windsurf-quickstart.md +10 -7
  22. package/docs/overrides/README.md +43 -0
  23. package/docs/overrides/styles.css +129 -0
  24. package/docs/quickstart-mmk.md +2 -2
  25. package/docs/quickstart-sdlc.md +5 -9
  26. package/docs/quickstart.md +1 -1
  27. package/docs/regenerate-guide.md +15 -4
  28. package/docs/releases/_manifest.json +1 -0
  29. package/docs/releases/v2026.6.13-announcement.md +6 -0
  30. package/docs/releases/v2026.7.13-announcement.md +38 -0
  31. package/package.json +6 -6
  32. package/prebuilt/fortemi-core/framework/aiwg-fortemi-index-v2.json +1 -1
  33. package/prebuilt/fortemi-core/framework/manifest.json +2 -2
  34. package/tools/agents/providers/base.mjs +9 -3
  35. package/tools/agents/providers/openhuman.mjs +14 -1
package/README.md CHANGED
@@ -499,6 +499,9 @@ aiwg use research # Research workflow automation (8 agents, 8-stage pip
499
499
  aiwg use rlm # RLM addon (recursive context decomposition)
500
500
  aiwg use all # Everything
501
501
 
502
+ # Existing projects: re-analyze and refresh the provider context bridge
503
+ aiwg regenerate
504
+
502
505
  # Or scaffold a new project
503
506
  aiwg new my-project
504
507
 
@@ -1,10 +1,9 @@
1
1
  id: research/citation-resolves
2
2
  name: Citation reference integrity
3
- description: Every REF-XXX reference in body text points to an existing note in findings/
3
+ description: Every REF reference in body text points to an existing note in the lint target
4
4
  severity: error
5
5
  applies-to:
6
6
  glob: "**/*.md"
7
7
  checks:
8
8
  - type: reference-resolves
9
- referencePattern: "REF-\\d{3}"
10
- basePath: findings
9
+ referencePattern: "\\bREF-\\d{3,}\\b"
@@ -12,7 +12,7 @@ commandHint:
12
12
 
13
13
  # Research Lint
14
14
 
15
- Run the research corpus lint ruleset against `.aiwg/research/` to detect structural and referential integrity issues.
15
+ Run the research corpus lint ruleset against the configured corpus to detect structural and referential integrity issues.
16
16
 
17
17
  ## Triggers
18
18
 
@@ -26,7 +26,12 @@ Run the research corpus lint ruleset against `.aiwg/research/` to detect structu
26
26
  ## Parameters
27
27
 
28
28
  ### `[target]` (optional)
29
- Path to lint. Defaults to `.aiwg/research/`.
29
+ Path to lint. When omitted, resolve the target in this order:
30
+
31
+ 1. `<AIWG_CORPUS_ROOT>/documentation/references/` when `AIWG_CORPUS_ROOT` is set.
32
+ 2. `<research.corpusRoot>/documentation/references/` when `.aiwg/aiwg.config` configures a corpus root.
33
+ 3. `documentation/references/` when the canonical warehouse layout exists in the project.
34
+ 4. `.aiwg/research/` for legacy project-local corpora.
30
35
 
31
36
  ### `--fix` (optional)
32
37
  Attempt auto-fixes for fixable issues (add missing frontmatter defaults, correct formatting).
@@ -44,10 +49,10 @@ Severity threshold for failure: `error` (default), `warn`, or `info`.
44
49
 
45
50
  ### Phase 1: Run Lint
46
51
 
47
- Execute the lint runner against the research corpus:
52
+ Resolve the default target as described above, then execute the lint runner against the research corpus:
48
53
 
49
54
  ```bash
50
- aiwg lint .aiwg/research/ --ruleset research --format full
55
+ aiwg lint <resolved-target> --ruleset research --format full
51
56
  ```
52
57
 
53
58
  This checks all 11 rules in the research ruleset:
@@ -57,7 +62,7 @@ This checks all 11 rules in the research ruleset:
57
62
  | `ref-frontmatter` | error | Required frontmatter fields present |
58
63
  | `ref-id-unique` | error | No duplicate REF-XXX identifiers |
59
64
  | `ref-id-format` | warn | REF identifiers follow `REF-NNN` naming |
60
- | `citation-resolves` | error | REF-XXX references point to existing notes |
65
+ | `citation-resolves` | error | Whole REF IDs point to exact or slugged notes anywhere in the lint target |
61
66
  | `grade-present` | warn | GRADE quality assessment in frontmatter |
62
67
  | `provenance-present` | warn | Provenance metadata present |
63
68
  | `cross-ref-bidirectional` | info | Related refs linked both ways |
@@ -23,8 +23,8 @@ npm run lint
23
23
  ## AIWG SDLC Framework
24
24
 
25
25
  This project uses [AIWG](https://aiwg.io) for SDLC orchestration. OpenHuman is
26
- AIWG-convention-aware: agent personas live in `.agents/agents/`, skills in
27
- `.openhuman/skills/`, and this file is the Discover-First bridge.
26
+ AIWG-convention-aware: kernel skills live in `~/.openhuman/skills/`, rule bodies
27
+ in `~/.openhuman/.aiwg/rules/`, and this file is the Discover-First bridge.
28
28
 
29
29
  ### Discover-First Protocol
30
30
 
@@ -41,17 +41,18 @@ corpus, not just the kernel set surfaced here.
41
41
 
42
42
  ### Skills
43
43
 
44
- Kernel skills deploy to `.openhuman/skills/` (OpenHuman's project-scope native
45
- scan root); standard skills are sequestered under `.openhuman/.aiwg/skills/`
46
- for index-driven discovery. Redeploy or refresh with:
44
+ Kernel skills deploy to `~/.openhuman/skills/` (OpenHuman's user-scope native
45
+ scan root). Standard skills stay in AIWG's index-driven discovery system rather
46
+ than being copied into an OpenHuman scan root. Redeploy or refresh with:
47
47
 
48
48
  ```bash
49
49
  aiwg use sdlc --provider openhuman
50
50
  aiwg refresh --provider openhuman
51
51
  ```
52
52
 
53
- Project-scope skill discovery requires the workspace trust marker under
54
- `.openhuman/` — see the quickstart.
53
+ The default deploy emits no markdown persona copies. Curated OpenHuman-native
54
+ TOML workers are optional via `--harness-agents` and land under
55
+ `~/.openhuman/agents/`.
55
56
 
56
57
  ### Commands & Rules
57
58
 
@@ -59,8 +60,8 @@ OpenHuman has no native command surface; AIWG commands surface through this
59
60
  `AGENTS.md` bridge and the deployed command-skills. Rule full bodies are on
60
61
  disk under `.openhuman/.aiwg/rules/` and reachable via `aiwg show rule <name>`.
61
62
 
62
- CRITICAL/HIGH rules are deployed to disk as always-on bodies. The MEDIUM/LOW
63
- tier is on-demand only — listed below, fetched via `aiwg show rule <name>`.
63
+ CRITICAL/HIGH rules are rendered into the active context. The MEDIUM/LOW tier
64
+ is on-demand only — listed below, fetched via `aiwg show rule <name>`.
64
65
 
65
66
  {{ON_DEMAND_RULES}}
66
67
 
@@ -28,10 +28,56 @@ function parseFrontmatter(content) {
28
28
  }
29
29
  return result;
30
30
  }
31
+ const DEFAULT_REFERENCE_PATTERN = '\\bREF-\\d{3,}\\b';
32
+ /**
33
+ * Build a target-wide artifact ID index once per lint run.
34
+ *
35
+ * Reference documents may use either an exact filename (`REF-001.md`) or the
36
+ * canonical slugged form (`REF-001-some-title.md`). Indexing the ID prefix also
37
+ * makes resolution independent of a particular corpus directory layout.
38
+ */
39
+ function buildReferenceIndex(allFiles) {
40
+ const references = new Set();
41
+ for (const file of allFiles) {
42
+ if (path.extname(file).toLowerCase() !== '.md')
43
+ continue;
44
+ const basename = path.basename(file, '.md');
45
+ if (/^REF-\d+-(?:citations|radar)$/.test(basename))
46
+ continue;
47
+ references.add(basename);
48
+ const artifactId = basename.match(/^(.+?-\d+)(?:-|$)/);
49
+ if (artifactId)
50
+ references.add(artifactId[1]);
51
+ }
52
+ return references;
53
+ }
54
+ /**
55
+ * Preserve support for references stored outside the lint target when a rule
56
+ * explicitly supplies basePath. Accept both exact and slugged filenames.
57
+ */
58
+ function resolvesFromBasePath(refId, targetDir, basePath) {
59
+ const candidateDirs = new Set([
60
+ path.resolve(targetDir, basePath),
61
+ path.resolve(process.cwd(), basePath),
62
+ ]);
63
+ for (const dir of candidateDirs) {
64
+ try {
65
+ if (fs.existsSync(path.join(dir, `${refId}.md`)))
66
+ return true;
67
+ if (fs.readdirSync(dir).some(file => file.startsWith(`${refId}-`) && file.endsWith('.md'))) {
68
+ return true;
69
+ }
70
+ }
71
+ catch {
72
+ // Missing or unreadable candidate directories do not resolve the ID.
73
+ }
74
+ }
75
+ return false;
76
+ }
31
77
  /**
32
78
  * Run a single check against a file's content and frontmatter
33
79
  */
34
- function runCheck(check, content, frontmatter, filePath, targetDir, allFiles) {
80
+ function runCheck(check, content, frontmatter, filePath, targetDir, allFiles, referenceIndex) {
35
81
  const diagnostics = [];
36
82
  switch (check.type) {
37
83
  case 'frontmatter-required': {
@@ -68,7 +114,7 @@ function runCheck(check, content, frontmatter, filePath, targetDir, allFiles) {
68
114
  break;
69
115
  }
70
116
  case 'reference-resolves': {
71
- const refPattern = check.referencePattern || 'REF-\\d{3}';
117
+ const refPattern = check.referencePattern || DEFAULT_REFERENCE_PATTERN;
72
118
  const regex = new RegExp(refPattern, 'g');
73
119
  const lines = content.split('\n');
74
120
  for (let i = 0; i < lines.length; i++) {
@@ -76,20 +122,15 @@ function runCheck(check, content, frontmatter, filePath, targetDir, allFiles) {
76
122
  while ((match = regex.exec(lines[i])) !== null) {
77
123
  const refId = match[0];
78
124
  const basePath = check.basePath || '.aiwg/research/findings';
79
- const refFile = path.join(targetDir, basePath, `${refId}.md`);
80
- if (!fs.existsSync(refFile)) {
81
- // Also check relative to cwd
82
- const altPath = path.join(process.cwd(), basePath, `${refId}.md`);
83
- if (!fs.existsSync(altPath)) {
84
- diagnostics.push({
85
- ruleId: '',
86
- ruleName: '',
87
- severity: 'error',
88
- file: filePath,
89
- message: `Reference '${refId}' does not resolve to an existing file`,
90
- line: i + 1,
91
- });
92
- }
125
+ if (!referenceIndex.has(refId) && !resolvesFromBasePath(refId, targetDir, basePath)) {
126
+ diagnostics.push({
127
+ ruleId: '',
128
+ ruleName: '',
129
+ severity: 'error',
130
+ file: filePath,
131
+ message: `Reference '${refId}' does not resolve to an existing file`,
132
+ line: i + 1,
133
+ });
93
134
  }
94
135
  }
95
136
  }
@@ -149,7 +190,7 @@ function runCheck(check, content, frontmatter, filePath, targetDir, allFiles) {
149
190
  break;
150
191
  }
151
192
  case 'cross-ref-bidirectional': {
152
- const refPattern = check.referencePattern || 'REF-\\d{3}';
193
+ const refPattern = check.referencePattern || DEFAULT_REFERENCE_PATTERN;
153
194
  const regex = new RegExp(refPattern, 'g');
154
195
  const currentBasename = path.basename(filePath, '.md');
155
196
  const matches = content.match(regex) || [];
@@ -185,7 +226,7 @@ function runCheck(check, content, frontmatter, filePath, targetDir, allFiles) {
185
226
  /**
186
227
  * Run a single rule against all matching files in the target
187
228
  */
188
- async function runRule(rule, targetDir, allFiles) {
229
+ async function runRule(rule, targetDir, allFiles, referenceIndex) {
189
230
  const diagnostics = [];
190
231
  // Filter files matching the rule's glob
191
232
  const ruleGlob = rule.appliesTo.glob;
@@ -226,7 +267,7 @@ async function runRule(rule, targetDir, allFiles) {
226
267
  for (const check of rule.checks) {
227
268
  if (check.type === 'id-unique')
228
269
  continue; // Already handled
229
- const checkDiagnostics = runCheck(check, content, frontmatter, file, targetDir, allFiles);
270
+ const checkDiagnostics = runCheck(check, content, frontmatter, file, targetDir, allFiles, referenceIndex);
230
271
  for (const d of checkDiagnostics) {
231
272
  d.ruleId = rule.id;
232
273
  d.ruleName = rule.name;
@@ -286,10 +327,11 @@ function autoDetectRulesets(target, available) {
286
327
  export async function runLint(targetDir, rulesets, options = {}) {
287
328
  const recursive = options.recursive ?? true;
288
329
  const allFiles = await collectFiles(targetDir, recursive);
330
+ const referenceIndex = buildReferenceIndex(allFiles);
289
331
  const allDiagnostics = [];
290
332
  for (const ruleset of rulesets) {
291
333
  for (const rule of ruleset.rules) {
292
- const diagnostics = await runRule(rule, targetDir, allFiles);
334
+ const diagnostics = await runRule(rule, targetDir, allFiles, referenceIndex);
293
335
  allDiagnostics.push(...diagnostics);
294
336
  }
295
337
  }
@@ -461,7 +461,7 @@ aiwg use sdlc --ci-hooks-enabled --dry-run
461
461
  | OpenCode | `opencode` | `.opencode/agent/`, `.opencode/commands/`, `.opencode/skill/`, `.opencode/rule/` | — |
462
462
  | Hermes | `hermes` | `~/.hermes/skills/`, `AGENTS.md` (lean) | — |
463
463
  | OpenClaw | `openclaw` | `~/.openclaw/agents/`, `~/.openclaw/commands/`, `~/.openclaw/skills/`, `~/.openclaw/rules/`, `~/.openclaw/behaviors/` | ✓ |
464
- | OpenHuman | `openhuman` | `.agents/agents/`, `~/.openhuman/skills/`, `~/.openhuman/.aiwg/{skills,rules}/`, `AGENTS.md` | — |
464
+ | OpenHuman | `openhuman` | `~/.openhuman/skills/`, `~/.openhuman/.aiwg/rules/`, optional `~/.openhuman/agents/aiwg_*.toml`, project `AGENTS.md` | — |
465
465
  | Local/Ollama | `local` | Same as `claude` (local model, Claude Code paths) | — |
466
466
 
467
467
  **Commands → Skills migration:**
@@ -473,6 +473,7 @@ On first run after the commands-to-skills migration, `aiwg use` detects an exist
473
473
  - **Codex**: Commands and skills deploy to `~` (user-level) for availability across all projects; the provider ID is `codex`, not `openai`
474
474
  - **Windsurf**: Agents aggregated into `AGENTS.md` at project root; no separate agent files
475
475
  - **Warp**: Agents and commands also aggregated into `WARP.md` for single-file context loading
476
+ - **OpenHuman**: Kernel skills and rule bodies are user-global; the default deploy emits no markdown persona copies. Project context is rendered into `AGENTS.md`, and curated native TOML agents are opt-in with `--harness-agents`.
476
477
  - **Hermes**: Not a spawnable CLI — access via `ollama run hermes3` or MCP sidecar; deploy sets up skills and a lean AGENTS.md
477
478
  - **OpenClaw**: Only provider with behaviors support (`~/.openclaw/behaviors/`); all artifacts deploy to home directory
478
479
  - **Local/Ollama**: Uses Claude Code path layout; specify `--coding-model ollama/<model>` to route coding tasks to the local model
@@ -1,240 +1,153 @@
1
1
  # CI/CD Secrets Configuration
2
2
 
3
- **Version:** 2.0
4
- **Last Updated:** 2026-07-12
3
+ **Version:** 3.0
4
+ **Last Updated:** 2026-07-15
5
5
  **Target Audience:** Repository maintainers and administrators
6
6
 
7
- AIWG's Gitea CI/CD workflows use OpenBao for repository-managed secrets. Gitea
8
- Actions stores only the OpenBao bootstrap pair:
7
+ AIWG's Gitea CI/CD workflows use vault for repository-managed secrets. Gitea
8
+ Actions stores only the vault bootstrap pair:
9
9
 
10
10
  | Tracker secret | Purpose |
11
11
  |---|---|
12
- | `BAO_CI_ROLE_ID` | AppRole role ID for the `ci-aiwg` reader. |
13
- | `BAO_CI_SECRET_ID` | AppRole secret ID for the `ci-aiwg` reader. |
12
+ | `VAULT_CI_ROLE_ID` | AppRole role ID for the `ci-aiwg` reader. |
13
+ | `VAULT_CI_SECRET_ID` | AppRole secret ID for the `ci-aiwg` reader. |
14
14
 
15
15
  Do not recreate legacy Gitea secrets such as `NPM_TOKEN`, `GH_ACCESS_TOKEN`,
16
- `AIWG_IO_DISPATCH_TOKEN`, or `DEPLOY_SSH_KEY`. Those values live in OpenBao and
17
- are fetched at runtime with [`../../ci/openbao-fetch.sh`](../../ci/openbao-fetch.sh).
16
+ `AIWG_IO_DISPATCH_TOKEN`, or `DOCSITE_DEPLOY_KEY`. Those values live in vault
17
+ and are fetched at runtime with [`../../ci/vault-fetch.sh`](../../ci/vault-fetch.sh).
18
18
 
19
19
  `secrets.GITHUB_TOKEN` is not part of this migration. It is a per-run token
20
20
  issued by the CI system and is still used by PR-comment workflows.
21
21
 
22
- ## OpenBao Fetch Specs
22
+ ## Vault Fetch Specs
23
23
 
24
24
  | Workflow | Spec | Exported value |
25
25
  |---|---|---|
26
- | `.gitea/workflows/npm-publish.yml` | `ci/openbao-fetch.npm-publish.spec` | `NODE_AUTH_TOKEN` |
27
- | `.gitea/workflows/gitea-release.yml` | `ci/openbao-fetch.gitea-release.spec` | `GITEA_TOKEN` |
28
- | `.gitea/workflows/upload-release-sigs.yml` | `ci/openbao-fetch.upload-release-sigs.spec` | `GITEA_TOKEN` |
29
- | `.gitea/workflows/github-mirror.yml` | `ci/openbao-fetch.github-mirror.spec` | `GH_TOKEN` |
30
- | `.gitea/workflows/notify-site.yml` | `ci/openbao-fetch.notify-site.spec` | `AIWG_IO_DISPATCH_TOKEN` |
31
- | `.gitea/workflows/docsite-deploy.yml` | `ci/openbao-fetch.docsite-deploy.spec` | `DEPLOY_SSH_KEY_FILE` |
26
+ | `.gitea/workflows/npm-publish.yml` | `ci/vault-fetch.npm-publish.spec` | `NODE_AUTH_TOKEN` |
27
+ | `.gitea/workflows/gitea-release.yml` | `ci/vault-fetch.gitea-release.spec` | `GITEA_TOKEN` |
28
+ | `.gitea/workflows/upload-release-sigs.yml` | `ci/vault-fetch.upload-release-sigs.spec` | `GITEA_TOKEN` |
29
+ | `.gitea/workflows/github-mirror.yml` | `ci/vault-fetch.github-mirror.spec` | `GH_TOKEN` |
30
+ | `.gitea/workflows/notify-site.yml` | `ci/vault-fetch.notify-site.spec` | `AIWG_IO_DISPATCH_TOKEN` |
31
+ | `.gitea/workflows/docsite-deploy.yml` | `ci/vault-fetch.docsite-deploy.spec` | `DOCSITE_DEPLOY_KEY_FILE` |
32
+
33
+ The specs contain only `*_VAULT_PATH` and `*_VAULT_FIELD` placeholders. Concrete
34
+ vault paths, metadata paths, hostnames, and fields are stored as private Gitea
35
+ variables or in the private operations catalog.
32
36
 
33
37
  Validate specs without reading live secrets:
34
38
 
35
39
  ```bash
36
- for f in ci/openbao-fetch.*.spec; do
37
- bash ci/openbao-fetch.sh --spec "$f" --dry-run
40
+ for f in ci/vault-fetch.*.spec; do
41
+ bash ci/vault-fetch.sh --spec "$f" --dry-run
38
42
  done
39
43
  ```
40
44
 
41
- ## Classification
42
-
43
- | Name | Class | Destination |
44
- |---|---|---|
45
- | `BAO_CI_ROLE_ID` | `BOOTSTRAP` | Gitea Actions secret |
46
- | `BAO_CI_SECRET_ID` | `BOOTSTRAP` | Gitea Actions secret |
47
- | `GITHUB_TOKEN` | CI-issued token | No storage; issued per run |
48
- | `NPM_TOKEN` | `SECRET` | `kv_internal/ci/aiwg/gitea-npm-token` field `token` |
49
- | `GH_ACCESS_TOKEN` | `SECRET` | `kv_internal/ci/aiwg/github-mirror-token` field `token` |
50
- | `AIWG_IO_DISPATCH_TOKEN` | `SECRET` | `kv_internal/ci/aiwg/aiwg-io-dispatch-token` field `token` |
51
- | `DEPLOY_SSH_KEY` | `SECRET` | `kv_internal/ci/shared/docs-deploy` field `private_key` |
52
- | `DEPLOY_HOST` | `CONFIG` | Gitea Actions variable |
53
- | `DEPLOY_PORT` | `CONFIG` | Gitea Actions variable |
54
- | `DEPLOY_USER` | `CONFIG` | Gitea Actions variable |
55
- | `DEPLOY_PATH` | `CONFIG` | Gitea Actions variable |
56
-
57
- ## AppRole and Path Plan
58
-
59
- The reader AppRole is `ci-aiwg`. Its token TTL should stay short, with a
60
- maximum TTL no longer than the release jobs need.
61
-
62
- The machine-readable plan is [`../../ci/openbao-migration-plan.json`](../../ci/openbao-migration-plan.json).
63
- The AppRole policy file is [`../../ci/openbao-ci-aiwg.hcl`](../../ci/openbao-ci-aiwg.hcl).
64
-
65
- Required readable leaves:
66
-
67
- ```text
68
- kv_internal/data/ci/aiwg/gitea-npm-token
69
- kv_internal/data/ci/aiwg/github-mirror-token
70
- kv_internal/data/ci/aiwg/aiwg-io-dispatch-token
71
- kv_internal/data/ci/shared/docs-deploy
72
- ```
73
-
74
- Adjacent repository paths should deny access. Verify with the OpenBao operator
75
- runbook in `itops/docs/security/ci-secret-migration.md`.
76
-
77
45
  Run the local consistency gate:
78
46
 
79
47
  ```bash
80
- npm run lint:openbao-migration
48
+ npm run lint:vault-migration
81
49
  ```
82
50
 
83
- After live leaves are provisioned, verify metadata without reading values:
84
-
85
- ```bash
86
- npm run lint:openbao-migration -- --live
87
- ```
88
-
89
- To induct leaf values from an approved local medium, prepare one file per leaf:
51
+ ## Classification
90
52
 
91
- ```text
92
- ci__aiwg__gitea-npm-token.token
93
- ci__aiwg__github-mirror-token.token
94
- ci__aiwg__aiwg-io-dispatch-token.token
95
- ci__shared__docs-deploy.private_key
96
- ```
53
+ | Name | Class | Destination |
54
+ |---|---|---|
55
+ | `VAULT_CI_ROLE_ID` | `BOOTSTRAP` | Gitea Actions secret |
56
+ | `VAULT_CI_SECRET_ID` | `BOOTSTRAP` | Gitea Actions secret |
57
+ | `GITHUB_TOKEN` | CI-issued token | No storage; issued per run |
58
+ | `GT_NPM_TOKEN_VAULT_PATH`, `GT_NPM_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
59
+ | `GT_RELEASE_TOKEN_VAULT_PATH`, `GT_RELEASE_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
60
+ | `GH_MIRROR_TOKEN_VAULT_PATH`, `GH_MIRROR_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
61
+ | `AIWG_IO_DISPATCH_TOKEN_VAULT_PATH`, `AIWG_IO_DISPATCH_TOKEN_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
62
+ | `DOCSITE_DEPLOY_KEY_VAULT_PATH`, `DOCSITE_DEPLOY_KEY_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
63
+ | `RELEASE_SIGNING_KEY_VAULT_PATH`, `RELEASE_SIGNING_KEY_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
64
+ | `RELEASE_SIGNING_PASSPHRASE_VAULT_PATH`, `RELEASE_SIGNING_PASSPHRASE_VAULT_FIELD` | `ROUTE` | Gitea Actions variables |
65
+ | `VAULT_ADDR` | `ROUTE` | Gitea Actions variable |
66
+ | `DOCSITE_DEPLOY_HOST`, `DOCSITE_DEPLOY_PORT`, `DOCSITE_DEPLOY_USER`, `DOCSITE_DEPLOY_PATH` | `CONFIG` | Gitea Actions variables |
97
67
 
98
- Preview the OpenBao writes:
68
+ The machine-readable variable manifest is
69
+ [`../../ci/vault-migration-plan.json`](../../ci/vault-migration-plan.json).
99
70
 
100
- ```bash
101
- npm run provision:openbao-migration -- --values-dir /path/to/approved/value-files
102
- ```
71
+ ## Provisioning
103
72
 
104
- Apply only after confirming the paths and metadata:
73
+ Use the private operations catalog to create the scoped policy and AppRole. Keep
74
+ the concrete policy file outside this repository, then pass it to:
105
75
 
106
76
  ```bash
107
- npm run provision:openbao-migration -- --values-dir /path/to/approved/value-files --apply
108
- ```
109
-
110
- Policy sketch:
111
-
112
- ```hcl
113
- path "kv_internal/data/ci/aiwg/*" {
114
- capabilities = ["read"]
115
- }
116
-
117
- path "kv_internal/metadata/ci/aiwg/*" {
118
- capabilities = ["read", "list"]
119
- }
120
-
121
- path "kv_internal/data/ci/shared/docs-deploy" {
122
- capabilities = ["read"]
123
- }
124
-
125
- path "kv_internal/metadata/ci/shared/docs-deploy" {
126
- capabilities = ["read"]
127
- }
77
+ npm run provision:vault-approle -- --policy-file /path/to/private-policy.hcl
78
+ VAULT_ADMIN_TOKEN=<admin-token> npm run provision:vault-approle -- \
79
+ --policy-file /path/to/private-policy.hcl \
80
+ --apply
128
81
  ```
129
82
 
130
- Provision the AppRole with short-lived tokens. If the `bao` CLI is unavailable,
131
- use the checked-in curl-based helper:
83
+ To induct or rotate values from an approved local medium, prepare one file per
84
+ route ID:
132
85
 
133
- ```bash
134
- npm run provision:openbao-approle
135
- BAO_TOKEN=<admin-token> npm run provision:openbao-approle -- --apply
86
+ ```text
87
+ gitea-npm-token.value
88
+ gitea-release-token.value
89
+ github-mirror-token.value
90
+ aiwg-io-dispatch-token.value
91
+ docsite-deploy-key.value
92
+ release-signing-key.value
93
+ release-signing-passphrase.value
136
94
  ```
137
95
 
138
- Equivalent `bao` CLI commands:
96
+ Preview the vault writes without printing paths:
139
97
 
140
98
  ```bash
141
- bao policy write ci-aiwg ci/openbao-ci-aiwg.hcl
142
- bao write auth/approle/role/ci-aiwg \
143
- token_policies=ci-aiwg \
144
- token_ttl=5m \
145
- token_max_ttl=15m \
146
- secret_id_ttl=0
99
+ npm run provision:vault-migration -- \
100
+ --routing-env /path/to/private-routing.env \
101
+ --values-dir /path/to/approved-value-files
147
102
  ```
148
103
 
149
- Generate the Gitea bootstrap handoff file without printing values:
104
+ Apply only after confirming the private routing env and value files:
150
105
 
151
106
  ```bash
152
- install -d -m 700 ~/.config/openbao/handoff
153
- umask 077
154
- {
155
- printf 'BAO_CI_ROLE_ID=%s\n' \
156
- "$(bao read -field=role_id auth/approle/role/ci-aiwg/role-id)"
157
- printf 'BAO_CI_SECRET_ID=%s\n' \
158
- "$(bao write -f -field=secret_id auth/approle/role/ci-aiwg/secret-id)"
159
- } > ~/.config/openbao/handoff/aiwg-ci.env
160
- chmod 600 ~/.config/openbao/handoff/aiwg-ci.env
161
- ```
162
-
163
- Prepare the non-secret deploy variables in a separate env file:
164
-
165
- ```text
166
- DEPLOY_HOST=docs-host.example.internal
167
- DEPLOY_PORT=22
168
- DEPLOY_USER=deploy
169
- DEPLOY_PATH=/srv/docs/aiwg
107
+ npm run provision:vault-migration -- \
108
+ --routing-env /path/to/private-routing.env \
109
+ --values-dir /path/to/approved-value-files \
110
+ --apply
170
111
  ```
171
112
 
172
- Validate the Gitea handoff without printing values:
113
+ Validate and apply the Gitea handoff without printing values:
173
114
 
174
115
  ```bash
175
- npm run configure:gitea-openbao -- \
176
- --bootstrap-env ~/.config/openbao/handoff/aiwg-ci.env \
177
- --vars-env /path/to/aiwg-deploy-vars.env
178
- ```
179
-
180
- Apply after `tea login` is configured for `git.integrolabs.net`:
116
+ npm run configure:gitea-vault -- \
117
+ --bootstrap-env ~/.config/vault/handoff/aiwg-ci.env \
118
+ --vars-env /path/to/private-routing-and-deploy.env
181
119
 
182
- ```bash
183
- npm run configure:gitea-openbao -- \
184
- --bootstrap-env ~/.config/openbao/handoff/aiwg-ci.env \
185
- --vars-env /path/to/aiwg-deploy-vars.env \
120
+ npm run configure:gitea-vault -- \
121
+ --bootstrap-env ~/.config/vault/handoff/aiwg-ci.env \
122
+ --vars-env /path/to/private-routing-and-deploy.env \
186
123
  --apply
187
124
  ```
188
125
 
189
126
  ## Docsite Deployment Config
190
127
 
191
- The `Docsite Deploy` workflow publishes the AIWG documentation tenant to
192
- `docs.aiwg.io` over SSH. The private key is fetched from OpenBao. The target
193
- connection details are non-secret Gitea Actions variables:
194
-
195
- | Variable | Purpose |
196
- |---|---|
197
- | `DEPLOY_HOST` | Docs host name. |
198
- | `DEPLOY_PORT` | SSH port. |
199
- | `DEPLOY_USER` | SSH user. |
200
- | `DEPLOY_PATH` | Root web path for the AIWG tenant on `docs.aiwg.io`. |
128
+ The `Docsite Deploy` workflow publishes the AIWG documentation tenant over SSH.
129
+ The private key is fetched from vault. Target connection details are Gitea
130
+ Actions variables and should not be hardcoded in workflow files.
201
131
 
202
132
  `GT_ACCESS_TOKEN` is not used by AIWG's current docsite workflows. The publisher
203
133
  is installed from npm as `@pagenary/publisher`; do not add a clone token back to
204
134
  the docsite workflows unless the publisher source model changes again.
205
135
 
206
- ## Shared `docs.aiwg.io` Tenants
207
-
208
- `docs.aiwg.io` is shared with sibling documentation tenants. AIWG owns the root
209
- tenant at `DEPLOY_PATH`; `roctinam/agentic-sandbox` owns the
210
- `agentic-sandbox/` subtree and should deploy to:
211
-
212
- ```text
213
- ${DEPLOY_PATH%/}/agentic-sandbox/
214
- ```
215
-
216
- AIWG's root deploy keeps `rsync --delete` enabled, but `docsite-deploy.yml`
217
- protects registered sibling tenants with rsync receiver-protect filters and a
218
- dry-run deletion check. Keep the protected-subpath list in
219
- `.gitea/workflows/docsite-deploy.yml` aligned with the tenant table in
220
- `.gitea/workflows/README.md`.
221
-
222
136
  ## Troubleshooting
223
137
 
224
- ### OpenBao Bootstrap Missing
138
+ ### Vault Bootstrap Missing
225
139
 
226
- If `BAO_CI_ROLE_ID` or `BAO_CI_SECRET_ID` is absent, secret-bearing Gitea jobs
140
+ If `VAULT_CI_ROLE_ID` or `VAULT_CI_SECRET_ID` is absent, secret-bearing Gitea jobs
227
141
  skip instead of attempting a partial publish. Add or rotate the AppRole
228
142
  bootstrap pair in Gitea Actions secrets.
229
143
 
230
144
  ### Fetch Fails with 403
231
145
 
232
146
  The `ci-aiwg` AppRole is missing the required path policy or is trying to read
233
- the wrong leaf. Compare the workflow's spec file with the OpenBao policy and
234
- verify adjacent paths still deny access.
147
+ the wrong leaf. Compare the workflow spec placeholder names with the private
148
+ vault routing env and policy.
235
149
 
236
150
  ### Gitea Registry Publish Fails
237
151
 
238
- Check `kv_internal/ci/aiwg/gitea-npm-token` metadata, token scope, and token
239
- freshness. The value should be a Gitea API token scoped for package publish and
240
- release API work, but it must remain OpenBao-only.
152
+ Check the private routing entry, token scope, and token freshness. The value
153
+ must remain vault-only.