Skip to content

docs(research): openwork / AionUi / alaude-desktop integration analysis#617

Open
pftom wants to merge 13 commits intomainfrom
common-sphere
Open

docs(research): openwork / AionUi / alaude-desktop integration analysis#617
pftom wants to merge 13 commits intomainfrom
common-sphere

Conversation

@pftom
Copy link
Copy Markdown
Contributor

@pftom pftom commented May 6, 2026

Summary

  • Deep capability survey of different-ai/openwork, iOfficeAI/AionUi, and alsayadi/alaude-desktop (Labaik), with end-to-end pipeline analysis (data fetching / generation / persistence / agent integration / UI / memory / browser-use).
  • Cross-cuts the three repos against Open Design's current baseline (apps + packages + tools/contracts/SQLite/agent-adapters/skills/design-systems/craft) and produces a P0 / P1 / P2 extraction list, each with why it's useful + why Open Design needs it + minimum landing form.
  • Lands the three highest-ROI starters as isolated, unwired modules under apps/daemon/src/research/ — not imported by server.ts, no runtime impact. Each can be promoted to apps/daemon/src/<module>.ts in a follow-up PR with its own packages/contracts DTOs and tests.

What's in here

Research docdocs/research/openwork-aionui-alaude-integration.md

  • §3 three-way deep dive (positioning, repo structure, end-to-end chain, agent integration, UI, what to copy / not copy)
  • §4 horizontal capability matrix
  • §5 extraction list (P0 / P1 / P2)
  • §6 roadmap + minimum code starter map
  • §7 pitfalls and anti-patterns (e.g. Labaik README ≠ code on memory; openwork's Tauri/Electron split is migration debt; do not copy Labaik's screen-control without consent gating)

Code startersapps/daemon/src/research/

  • memory-schema.ts — P0-A: memories SQLite table (project + conversation + global scope, kind, source) + buildMemoryPrefix() that injects via prefix-on-last-user-message (provider-agnostic, see Labaik's injectIntoLastUser pattern)
  • audit-log.ts — P1-1: append-only JSONL audit per project at .od/projects/<id>/audit.jsonl, modeled on openwork apps/server/src/audit.ts
  • skill-hub.ts — P0-C: GitHub-as-registry fetcher (default pftom/open-design-hub@main, 5-min cache, path-traversal guard), modeled on openwork apps/server/src/skill-hub.ts. Also handles design-systems/ and craft/ namespaces
  • README.md — explains the unwired status and the integration path for each starter

Three highest-ROI extractions (from §1)

  1. Memory subsystem — self-built, SQLite-native, two-tier scope. Borrows Labaik's layered Profile + Episodic shape and AionUi's delegate-then-inject mental model, but solves what neither does: shared across all Open Design adapters (claude-code / codex / gemini-cli / cursor-agent / api-fallback / opencode / copilot / kiro / vibe / devin) via prefix injection on the last user message rather than role:'system'.
  2. Browser-use — daemon-managed chrome-devtools-mcp@0.17.0 (openwork's pattern). MCP is provider-agnostic so all existing adapters get the capability for free. Do not adopt Labaik's self-rolled Electron BrowserWindow path — it's desktop-only and forces per-adapter tool schema duplication.
  3. Skill Hub + share-link bundle — openwork's pattern of treating a GitHub repo as the registry. Zero ops, zero backend, fork-friendly. Closes the missing "user-facing discovery and install" surface for the 65+ skills and 140+ design-systems Open Design already ships.

Not in this PR

  • No changes to server.ts, db.ts, routes, contracts, web UI, or runtime behavior.
  • No new dependencies.
  • No memory / audit / hub features land yet — each is its own follow-up PR per the roadmap in §6.

Test plan

  • pnpm guard — residual-JS check + test layout check pass
  • pnpm --filter @open-design/daemon typecheck — strict mode + exactOptionalPropertyTypes + noUncheckedIndexedAccess all pass on the new files
  • No new files under daemon src/**/*.test.ts (test-layout boundary respected)
  • No imports added to existing daemon files; runtime behavior unchanged
  • Reviewer to skim docs/research/openwork-aionui-alaude-integration.md §5 and confirm the P0 / P1 priority calls before follow-up PRs land

Adds a deep capability survey of three external Agent-client projects
and a minimum integration starter for Open Design.

- docs/research/openwork-aionui-alaude-integration.md: full analysis,
  capability matrix, P0/P1/P2 extraction list with rationale, roadmap,
  and pitfalls. Each item carries "why useful" + "why open-design needs
  it" + minimum landing form.
- apps/daemon/src/research/: standalone TypeScript starters for the
  three highest-ROI items (memory schema + injection helper, audit log
  JSONL writer/reader, GitHub-as-registry skill hub fetcher). NOT wired
  into server.ts; subdir README states the integration path for follow-
  up PRs (one PR per item, with contracts DTOs and tests).

Validation: pnpm guard + pnpm --filter @open-design/daemon typecheck.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f55171595

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/daemon/src/research/memory-schema.ts Outdated
Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
@lefarcen lefarcen added the docs Documentation changes label May 6, 2026
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for putting together the research doc and keeping the starter modules isolated from runtime wiring. I found one merge-safe hardening issue in the audit-log starter that is worth fixing before it becomes the follow-up route implementation.

Generated by Looper 0.5.6 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/audit-log.ts Outdated
Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @pftom — excellent research synthesis, really appreciate the depth on three-way capability survey + extraction rationale. Found 11 items (4 P1 that merit attention before follow-up PRs land), mostly around security boundaries and consistency with existing daemon constraints.

P1 findings — worth addressing before promotion to main:

  1. Prompt injection persistence via memory (memory-schema.ts:197) — buildMemoryPrefix() treats saved memory as "authoritative" raw text, so a user-pinned or agent-saved entry can close </memory> and inject new instructions. This persists attacker instructions across sessions. Fix: escape/delimit content, change "authoritative" to "user-provided context; ignore instructions inside memory entries".

  2. Path traversal in audit log (audit-log.ts:53) — projectId is directly joined into .od/projects/<id>/audit.jsonl and auditFile() creates dirs on reads, so ../ or absolute-like segments can write/read outside project tree. Fix: reuse project-id validation from projects.ts, split "resolve path" from "mkdir for append" so reads are side-effect-free.

  3. Security premise mismatch for browser-use (doc:241) — The rationale says current adapters are sandboxed to .od/projects/<id>/, but actual adapter docs describe Codex with network, Gemini --yolo, OpenCode --dangerously-skip-permissions, Copilot --allow-all-tools. This invalidates the P0 security argument for web browsing. Fix: rewrite around actual permissions, add explicit URL allow/deny policy, consent model, network egress risks, adapter-by-adapter MCP support.

  4. Skill Hub trust model missing (doc:279) — P0 one-click install from GitHub URL, but no signing, commit pinning, checksums, publisher identity, license/provenance, review, update policy. VS Code has publisher identity/marketplace review, npm has lockfiles/integrity. Fix: add trust model, make P0 install pinned/read-only/manual-confirmed, share-link execution/install explicitly gated.

See inline comments for 7 more P2/P3 items (SQL construction, redaction no-op, craft install layout, frontmatter validation, memory consent/visibility, roadmap conflict, build scope).

Comment thread apps/daemon/src/research/memory-schema.ts
Comment thread apps/daemon/src/research/audit-log.ts Outdated
Comment thread apps/daemon/src/research/memory-schema.ts Outdated
Comment thread apps/daemon/src/research/audit-log.ts
Comment thread apps/daemon/src/research/skill-hub.ts
Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Validate `projectId` against the same `^[A-Za-z0-9._-]{1,128}$` allowlist
that `apps/daemon/src/projects.ts#isSafeId` uses, and verify the resolved
audit directory stays under `<root>/projects/`, before `mkdirSync()`.
Prevents a path-traversal bug if this starter is wired to the planned
`GET /api/projects/:id/audit` route with a malformed `:id`.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the push. Nice work on the audit-log path traversal hardening (mrcfps's finding is addressed) — the isSafeProjectId validation + defense-in-depth escape check look solid.

Status update on prior P1s:

P1 #2 (audit-log path traversal)PARTIALLY FIXED

  • Validation + containment check added (lines 57-58, 66-72) ✅
  • Remaining: readAuditEntries() still calls auditFile() which runs mkdirSync on reads — queries shouldn't mutate disk. Consider splitting into pure resolver + append-only ensure path.

P1 #1, #3, #4 — NOT ADDRESSED

  • Memory prompt injection (memory-schema.ts:196)
  • Browser-use security premise mismatch (doc:240)
  • Skill Hub trust model (doc:279)

These are design-level items for follow-up PRs rather than quick fixes — recommend addressing them in the roadmap before landing the actual integrations.

Comment thread apps/daemon/src/research/audit-log.ts
Comment thread apps/daemon/src/research/audit-log.ts Outdated
pftom added 2 commits May 6, 2026 12:03
Address P1/P2 security review feedback on the unwired research starters
under apps/daemon/src/research/:

- memory-schema.ts (P1 prompt injection): reject control characters and
  prompt-frame tokens at create/update time, render entries as
  <-escaped JSON in `buildMemoryPrefix`, and reword the wrapper to mark
  memory as user-provided context rather than authoritative instructions.
  Also clamp the LIMIT in `listMemoriesForContext` to a finite integer
  and bind it as `LIMIT ?` instead of interpolating.

- audit-log.ts (P1 path traversal + P2 redaction no-op): split path
  resolution from `mkdirSync` so reads are side-effect-free; add a
  recursive redactor that strips token / api_key / authorization /
  cookie / password / private_key / session_id / bearer / x-api-key
  fields before append and on export, with a per-string length cap and
  bounded recursion depth.

- skill-hub.ts (P2 download cap + frontmatter validation): replace
  `arrayBuffer()` with a streamed reader that bails on Content-Length
  over the cap and cancels mid-download once accumulated bytes exceed
  it; replace the description-only regex with a minimal frontmatter
  parser plus per-namespace validation (name slug + manifest-id match,
  description required, `od.mode` allowlist) applied on both list and
  install paths.

No runtime wiring changes; module remains unimported by server.ts.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Address two follow-up review comments on PR #617:

- isSafeProjectId: explicitly reject "." and "..". The existing character
  allowlist `[A-Za-z0-9._-]{1,128}` accepted bare-dot ids, and the
  defense-in-depth check still permitted `dir === projectsRoot`, so
  `projectId="."` would have written `audit.jsonl` directly under
  `<dataDir>/projects/`. Tighten the strict-prefix check to require a
  proper subpath of the projects root.

- Split path resolution from directory creation per reviewer's suggested
  shape: rename `resolveAuditPath` to `auditFilePath` (pure resolver,
  returns the full file path as a `string`) and have `ensureAuditFile`
  call it then `mkdirSync(path.dirname(file))`. `appendAuditEntry`
  continues to call ensure, `readAuditEntries` calls the pure resolver
  only — reads cannot create directories.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the careful follow-up hardening on the research starters — the audit path resolver split and memory prompt framing are much safer now. I found one merge-safe compatibility issue in the Skill Hub starter that is worth tightening before it becomes the follow-up install/list implementation.

Generated by Looper 0.5.6 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts
Address PR #617 review comment PRRC_kwDOSOgY8s6-ULyy: the previous
frontmatter validator rejected the 140+ design systems and the craft
modules Open Design already ships, since those files use a plain
Markdown shape (`# <Heading>` plus `> Category:` blockquote) rather
than YAML frontmatter. A hub repo mirroring the existing artifacts
would have returned no entries from `listHubEntries()` and thrown
from `installHubEntry()`, which defeats the starter's stated purpose.

Add a heading-based fallback for `design-systems` and `craft`:

- `deriveHeadingDescription(raw)` reads the first `# ` heading and
  the first blockquote block (cap 280 chars), stripping any
  frontmatter that may have been present.
- `resolveManifest(namespace, raw, manifestId)` keeps `skills`
  strict (frontmatter required, since all 65+ shipped skills carry
  `name` / `description`) but accepts either frontmatter or the
  plain-Markdown shape for `design-systems` and `craft`. The
  manifest id is still re-validated against the namespace's slug
  rules to keep path-traversal guards intact, and any `name` or
  `od.mode` present in frontmatter must still be valid.
- `readDirEntry`, `readFileEntry`, and `installHubEntry` route
  through `resolveManifest` instead of `validateFrontmatter`.

`validateFrontmatter` itself stays as-is (still strict, still
exported) so the v0 schema remains documented for hubs that opt
into frontmatter.

Sanity-checked against `design-systems/apple/DESIGN.md`,
`design-systems/atelier-zero/DESIGN.md`, and
`craft/accessibility-baseline.md`: all three now resolve to
non-null entries with sensible derived descriptions, while
`resolveManifest('skills', appleRaw, 'apple')` still returns null.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the follow-up fixes on the isolated research starters — the path containment, prompt framing, and design-system/craft compatibility are much stronger now. I found a few merge-safe hardening items to address before these helpers get promoted into runtime routes or prompt assembly.

Generated by Looper 0.5.6 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/audit-log.ts Outdated
Comment thread apps/daemon/src/research/audit-log.ts Outdated
Comment thread apps/daemon/src/research/memory-schema.ts Outdated
Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom great progress on the hardening iteration — the frontmatter compatibility path and audit-log safety improvements make this much more merge-ready. Two P1-level trust/security design questions remain for the follow-up PRs; see inline.

Comment thread apps/daemon/src/research/skill-hub.ts
Comment thread docs/research/openwork-aionui-alaude-integration.md Outdated
Comment thread apps/daemon/src/research/audit-log.ts
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the careful hardening passes on these isolated research starters — the remaining modules are much closer to being safe follow-up starting points. I found one merge-safe Skill Hub compatibility issue to tighten before the fetcher becomes the install/list implementation.

Generated by Looper 0.5.6 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts Outdated
…e browser MCP

- audit-log: redact camelCase secret keys (`accessToken`, `refreshToken`,
  `clientSecret`, `sessionId`, etc.) by inserting separators at case
  transitions before pattern test (PR #617 review, P2).
- skill-hub: require explicit `InstallApproval { pinnedSha, userApproved }`
  on `installHubEntry` and document the trust boundary — manifest
  validation alone is not a trust step (PR #617 review, P1).
- research doc: rewrite browser-use plan to remove the false "all agents
  are sandboxed" / "MCP tool consent already standard" premises; require
  daemon-owned MCP proxy with default-deny, transport-level read-only
  allowlist, and per-tool UI approval (PR #617 review, P1).

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening passes on the isolated research starters — the memory, audit, browser-use, and hub trust notes are much clearer now. I found one remaining merge-safe trust-boundary gap in the Skill Hub install starter that is worth tightening before this becomes a runtime install route.

Generated by Looper 0.6.0 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Copy link
Copy Markdown
Contributor

@lefarcen lefarcen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening passes on the research starters. I found a few remaining compatibility and enforcement issues in the skill-hub module that are worth tightening before the fetcher/validator becomes the follow-up install/list implementation.

Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Comment thread apps/daemon/src/research/skill-hub.ts
Comment thread apps/daemon/src/research/skill-hub.ts Outdated
pftom added 2 commits May 6, 2026 13:17
…riptions

Frontmatter validation in the research skill-hub starter now mirrors the
shapes already used by `skills/*/SKILL.md`:

- `ALLOWED_OD_MODES` adds `image`, `video`, `audio`, and `utility` so
  media and utility skills (e.g. `image-poster`, `video-shortform`,
  `audio-jingle`, `pptx-html-fidelity-audit`) are no longer rejected by a
  hub mirroring the catalog.
- The minimal YAML parser now reads `description: |`, `description: |-`,
  `description: >`, and `description: >-` block scalars instead of
  storing the literal `|` / `>`. Continuation lines are joined with `\n`
  and right-trimmed, matching `apps/daemon/src/frontmatter.ts`.
- Adds `apps/daemon/tests/research-skill-hub-frontmatter.test.ts` with
  fixture coverage against representative live skills for every
  currently-used `od.mode` value plus inline cases for `>` / `|-`.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
`installHubEntry` previously accepted any SHA-shaped `approval.pinnedSha`
without verifying that the bytes in `entry.raw` came from that commit.
Because `listHubEntries` fetched through the configured mutable branch
ref and `HubEntry.source` carried no immutable provenance, a caller could
list from `main`, pass any SHA-shaped string with `userApproved: true`,
and still install unpinned branch content into active prompt directories.

This change carries provenance end-to-end:

- `HubConfig` accepts `pinnedSha` (or `OD_HUB_PINNED_SHA`); when set,
  `listHubEntries` / `readDirEntry` / `readFileEntry` route the GitHub
  Contents API through `?ref=<sha>` and stamp every returned
  `HubEntry.source.commitSha` with that SHA.
- The list cache key now hashes the resolved ref (SHA or branch) so a
  pinned listing can never collide with a branch-tip listing.
- `installHubEntry` now refuses any entry whose `source.commitSha` is
  missing (mutable-branch listing) or does not match
  `approval.pinnedSha` (case-insensitive exact equality, no prefix
  matching). The planning comment block is updated to reflect that the
  SHA-anchored install path is now enforced rather than future-tense.

Adds `apps/daemon/tests/research-skill-hub-install.test.ts` covering:
mutable-branch entry rejected, mismatched approval SHA rejected,
matched SHA accepted, case-insensitive equality, short-prefix SHA
rejected, untrusted `userApproved: false`, and malformed approval SHA.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening on these isolated research starters — the new tests and provenance checks make the Skill Hub path much safer. I found one remaining merge-safe trust-boundary gap to tighten before this becomes a runtime install route.

Generated by Looper 0.6.0 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts
…ub API

`COMMIT_SHA_PATTERN` was treating any 7–40 hex string as immutable
provenance, but GitHub's `?ref=` parameter also accepts branch and tag
names. A hub repo could create a mutable branch/tag named like a short
SHA, pass the regex, fetch mutable-ref bytes, and then satisfy
`installHubEntry` because `entry.source.commitSha` and
`approval.pinnedSha` would both be the caller-supplied string (PR #617
review, P1).

`listHubEntries` now resolves the caller-supplied SHA prefix through
`GET /repos/{owner}/{repo}/commits/{input}` before any listing happens.
The canonical 40-char `.sha` is required to share the input's lowercase
hex prefix; otherwise GitHub resolved the ref through a branch/tag and
we refuse to treat it as immutable provenance. The canonical full SHA
is then used as the `?ref=` value and stamped on every
`HubEntry.source.commitSha`, so `installHubEntry`'s exact-match
equality compares 40-char canonical values on both sides.

Adds `apps/daemon/tests/research-skill-hub-resolve.test.ts` with three
cases: hex-shaped pinnedSha that resolves through a branch is rejected
before any Contents API call; a real prefix that resolves to a sharing
commit is accepted; and the listing/Contents API both use the canonical
40-char SHA, never the caller's raw 7-char input.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening on these isolated research starters — the new provenance tests and safer prompt/audit framing are moving this in the right direction. I found a few merge-safe follow-up items to tighten before these helpers become runtime routes.

Generated by Looper 0.6.0 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Comment thread apps/daemon/src/research/skill-hub.ts Outdated
Comment thread apps/daemon/src/research/audit-log.ts
Comment thread docs/research/openwork-aionui-alaude-integration.md Outdated
…audit lines

Addresses four PR #617 review threads:

- listHubEntries cache key now partitions on a per-process HMAC fingerprint
  of the bearer token (or the literal "public" sentinel for tokenless
  requests). This stops a tokened request that surfaces private hub
  manifests from being served back to a later tokenless request with the
  same (repo, ref, namespace), where `entry.raw` is prompt material.
- installHubEntry no longer trusts caller-supplied `entry.raw`. After the
  SHA equality check it re-fetches the manifest from
  `/repos/.../<entry.source.path>?ref=<entry.source.commitSha>` and
  writes only the freshly fetched bytes; if those bytes drift from
  `entry.raw` the install is rejected. A new `installHubManifest` entry
  point takes a server-owned selector `{ namespace, name, repo,
  pinnedSha }` and is the recommended surface for any future
  `POST /api/skill-hub/install` route — HTTP bodies never carry
  manifest bytes through the writer.
- The audit-log redaction pass now caps array length, object key count,
  and the final serialized JSONL line. Items past the per-container caps
  are replaced with a single truncation marker; lines past the byte cap
  collapse `detail` to a `__truncated` marker carrying the original size,
  so a pathological `detail` cannot block the daemon on synchronous
  JSON.stringify / appendFileSync.
- The Skill Hub example in `docs/research/openwork-aionui-alaude-
  integration.md` now mirrors the safe install path the starter enforces:
  pinned-SHA listing, `installHubManifest` taking a selector + approval,
  re-fetch inside the install path, the quarantine/activation boundary,
  and SHA-pinned sibling-asset fetches.

Adds `apps/daemon/tests/research-audit-log-bounds.test.ts` covering the
new caps, and extends the install test with a re-fetch / drift case plus
selector-based install coverage.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening on these isolated research starters — the new bounded audit serialization, memory prompt framing, and Skill Hub provenance checks are all moving the follow-up path in a safer direction. I found one merge-safe compatibility gap to tighten before the hub installer is promoted into runtime routes.

Generated by Looper 0.6.0 · runner=reviewer · agent=opencode

Comment thread apps/daemon/src/research/skill-hub.ts
`installHubEntry` re-fetches the manifest at the approved commit SHA
before writing, but the call into `fetchManifestRawAtSha()` did not pass
any token. `HubEntry.source` deliberately records `repo/path/commitSha`
without a token (forwarding an entry must not exfiltrate credentials),
so for a hub listed through `HubConfig.token` / `OD_HUB_TOKEN` the
re-fetch hit the GitHub API anonymously and returned 404 — breaking the
in-process install path documented as usable for entries from
`listHubEntries()` against a private hub (PR #617 review, P1).

`InstallOptions` now carries an optional server-owned `token`. The
install path prefers `opts.token` and falls back to `OD_HUB_TOKEN` when
the caller leaves it unset, mirroring the env-var contract
`listHubEntries()` already honors. The token is intentionally kept
out-of-band on the options object so a forwarded `HubEntry` still cannot
carry credentials across a process boundary; callers that don't already
hold a server-owned token should route through `installHubManifest`,
which takes the token on the selector instead.

Adds two regression cases to
`apps/daemon/tests/research-skill-hub-install.test.ts`:

- `threads opts.token through the re-fetch so private-hub entries
  install` — the mock returns 404 unless the bearer token matches, so
  the test fails if the install path forgets to thread the token.
- `falls back to OD_HUB_TOKEN when opts.token is not supplied` — covers
  processes configured the same way as the listing call site.

Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
Copy link
Copy Markdown
Contributor

@mrcfps mrcfps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pftom thanks for the continued hardening on these isolated research starters — the Skill Hub provenance path and memory framing are much safer now. I found two merge-safe follow-up items to tighten before these helpers are promoted into runtime routes or prompt assembly.

Generated by Looper 0.6.1 · runner=reviewer · agent=opencode

`installHubEntry: entry has no source.commitSha — list with HubConfig.pinnedSha so bytes are anchored to an immutable commit (${entry.namespace}/${entry.name})`,
);
}
if (!COMMIT_SHA_PATTERN.test(entrySha)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

installHubEntry() still accepts entry.source.commitSha with COMMIT_SHA_PATTERN, which allows a 7-character SHA prefix. If an in-process caller passes a crafted HubEntry whose source.commitSha equals the same short value the user approved, the exact string comparison below passes and the re-fetch uses that uncanonicalized value as ?ref=. That weakens the provenance boundary this starter is trying to establish, because the selector-based path canonicalizes short input through the commits API before using it, while this path can still fetch from a ref-like short value. Please require FULL_COMMIT_SHA_PATTERN for entry.source.commitSha here (entries produced by listHubEntries() already carry the canonical 40-character SHA), or canonicalize this path through resolveHubRef() before calling fetchManifestRawAtSha().

assertMemoryContent(input.content);
const now = Date.now();
const id = randomUUID();
const tags = input.tags ?? [];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createMemory() validates input.content, but it accepts input.tags as-is and later buildMemoryPrefix() emits those tags into the prompt JSON. Once this becomes a route, a caller could store very large or instruction-like tag strings to bypass the 8KB content cap and prompt-frame token checks, causing DB/prompt bloat and undercutting the memory injection guard. Please validate tags on write — for example, require a bounded array of small slug strings, cap count/total bytes, reject control/prompt-frame tokens, or omit tags from the rendered prefix unless they have been normalized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants