Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
e23d9ef
feat(tool-capability): admit external Claude plugins fail-closed
seonghobae Sep 8, 2026
469efe7
fix(tool-capability): document public admission contracts
seonghobae Sep 8, 2026
58f8bba
test(tool-capability): reject all live catalog identity drift
seonghobae Sep 8, 2026
6b7b5d6
test(tool-capability): isolate live catalog identity drift
seonghobae Sep 8, 2026
2987e86
test(tool-capability): reject forged activation authority
seonghobae Sep 8, 2026
572aa59
fix(tool-capability): revalidate activation and catalog authority
seonghobae Sep 8, 2026
632c77b
test(tool-capability): reject fabricated admission authority
seonghobae Sep 8, 2026
f9be6fb
fix(tool-capability): seal admitted extension authority
seonghobae Sep 8, 2026
3b072f8
test(tool-capability): require minted activation authority
seonghobae Sep 8, 2026
0323a3c
test(tool-capability): require live receipt authority
seonghobae Sep 8, 2026
81bff2f
fix(tool-capability): bind issued runtime authority
seonghobae Sep 8, 2026
7ca9aeb
test(tool-capability): reject self-asserted approval grants
seonghobae Sep 8, 2026
c6e91db
fix(tool-capability): bind external grants to Noema policy authority
seonghobae Sep 8, 2026
9b6d2dc
test(tool-capability): cover Noema policy approval boundary
seonghobae Sep 8, 2026
935b99e
docs(tool-capability): record independent policy issuance
seonghobae Sep 8, 2026
4ae1ea0
fix(tool-capability): preserve policy boundary oracles
seonghobae Sep 8, 2026
669a45a
merge(main): restack tool-capability admission after #556
seonghobae Sep 8, 2026
68f2843
docs: preserve tool-capability changelog across restack
seonghobae Sep 8, 2026
2096510
test(tool-capability): reject hostile public boundary envelopes
seonghobae Sep 8, 2026
83a54b6
fix(tool-capability): normalize hostile public boundary reads
seonghobae Sep 8, 2026
53eab96
test(tool-capability): reject hostile policy resolver accessors
seonghobae Sep 8, 2026
ae10728
fix(tool-capability): normalize hostile policy lookup
seonghobae Sep 8, 2026
0dd7956
test(tool-capability): require explicit active extension grant
seonghobae Sep 8, 2026
2f7ff5a
fix(tool-capability): keep source grant pilot-bounded
seonghobae Sep 8, 2026
1e8c8ba
test(tool-capability): require explicit active fixture authority
seonghobae Sep 8, 2026
9b4640a
test(tool-capability): restore activation policy fixture
seonghobae Sep 8, 2026
cdcc063
test(tool-capability): restore catalog policy fixture
seonghobae Sep 8, 2026
9711e3f
test(tool-capability): reject pre-activation invocation
seonghobae Sep 8, 2026
37dd2c0
fix(tool-capability): preserve activation chronology
seonghobae Sep 8, 2026
281a9db
docs(tool-capability): record invocation chronology
seonghobae Sep 8, 2026
f30f673
test(tool-capability): reject revoked admission capability lists
seonghobae Sep 8, 2026
802b0bf
fix(tool-capability): normalize hostile admission envelopes
seonghobae Sep 8, 2026
4be371e
test(tool-capability): reject backdated extension expiry bypass
seonghobae Sep 8, 2026
2b50b35
fix(tool-capability): bind extension expiry to runtime clock
seonghobae Sep 8, 2026
83e3130
test(tool-capability): cover runtime validity window boundaries
seonghobae Sep 8, 2026
f8703e6
docs(adr): bind plugin expiry to runtime time authority
seonghobae Sep 8, 2026
cb8ad63
test(tool-capability): reject divergent invocation replay
seonghobae Sep 8, 2026
9c7ae13
fix(tool-capability): bind replay to invocation request
seonghobae Sep 8, 2026
03c2a69
refactor(tool-capability): keep replay binding coverage-complete
seonghobae Sep 8, 2026
83ee8e9
test(tool-capability): keep core replay conflict covered
seonghobae Sep 8, 2026
5a50a9b
test(tool-capability): reject unbound core replay authority
seonghobae Sep 8, 2026
cbb64de
fix(tool-capability): bind public replay receipt authority
seonghobae Sep 8, 2026
8ff77d9
test(tool-capability): reject activation after policy revocation
seonghobae Sep 8, 2026
8251d4b
fix(tool-capability): revalidate policy before activation
seonghobae Sep 8, 2026
abcd1fe
test(tool-capability): reject invocation authority substitution
seonghobae Sep 8, 2026
f8814b8
fix(tool-capability): bind invocation to admitted authority
seonghobae Sep 8, 2026
1acbf2f
test(tool-capability): preserve bound-authority drift coverage
seonghobae Sep 8, 2026
feed68d
docs(tool-capability): add external extension operations boundary
seonghobae Sep 8, 2026
532cfaa
test(tool-capability): retain admission-bound authority fixtures
seonghobae Sep 8, 2026
ab2baed
test(tool-capability): mutate bound policy authority in drift fixtures
seonghobae Sep 8, 2026
273aa71
test(tool-capability): make authority fixtures explicit
seonghobae Sep 8, 2026
0a32ee0
test(tool-capability): reject cross-admission activation reuse
seonghobae Sep 8, 2026
4911530
test(tool-capability): bind replay authority to exact admission
seonghobae Sep 8, 2026
225a04e
fix(tool-capability): bind receipts to exact admission
seonghobae Sep 8, 2026
84a93a2
fix(tool-capability): bind activation and replay to exact admission
seonghobae Sep 8, 2026
2e84382
refactor(tool-capability): keep admission binding in core
seonghobae Sep 8, 2026
3f41d94
test(tool-capability): reject plaintext replay fingerprint retention
seonghobae Sep 8, 2026
79182c7
fix(tool-capability): retain only replay envelope digest
seonghobae Sep 8, 2026
ac6b6c0
docs(adr): define replay digest retention lifecycle
seonghobae Sep 8, 2026
1f51f43
test(tool-capability): reject owned SHA-256 primitive
seonghobae Sep 8, 2026
b50b430
test(tool-capability): accept audited or platform SHA-256 provider
seonghobae Sep 8, 2026
80292ed
fix(tool-capability): delegate replay digest to Web Crypto
seonghobae Sep 8, 2026
396514c
fix(tool-capability): await platform replay digest before publication
seonghobae Sep 8, 2026
d1af4d7
test(tool-capability): verify Web Crypto replay digest vectors
seonghobae Sep 8, 2026
210bc59
test(tool-capability): await accepted invocation before replay proof
seonghobae Sep 8, 2026
90bf018
test(tool-capability): await Web Crypto replay equality
seonghobae Sep 8, 2026
62d4ea3
docs(adr): delegate replay hashing to Worker Web Crypto
seonghobae Sep 8, 2026
a220003
test(tool-capability): await Web Crypto invocation admission
seonghobae Sep 8, 2026
26634f1
test(tool-capability): await Web Crypto policy invocation
seonghobae Sep 8, 2026
699a0d1
fix(tool-capability): await replay digest failures
seonghobae Sep 8, 2026
739dd8e
test(tool-capability): preserve pre-digest sync rejection
seonghobae Sep 8, 2026
6ab6056
test(tool-capability): cover Web Crypto provider failure
seonghobae Sep 8, 2026
d015e70
test(tool-capability): reject mutating policy approval getters
seonghobae Sep 8, 2026
3c0c4bc
fix(tool-capability): snapshot policy approval atomically
seonghobae Sep 8, 2026
ee0ccef
test(tool-capability): consolidate exact fail-closed coverage
seonghobae Sep 8, 2026
675bf99
test(tool-capability): cover hostile policy accessor failure
seonghobae Sep 8, 2026
ab0c6e7
test(tool-capability): expose activation policy accessor TOCTOU
seonghobae Sep 8, 2026
58c89ce
fix(tool-capability): snapshot activation authority once
seonghobae Sep 8, 2026
2e6b4b3
test(tool-capability): assert sealed activation policy snapshot
seonghobae Sep 8, 2026
168637d
test(tool-capability): expose future-dated authority events
seonghobae Sep 8, 2026
2d9bc08
fix(tool-capability): bind event time to trusted runtime clock
seonghobae Sep 8, 2026
a4d33ed
test(tool-capability): bind public timing fixtures to trusted runtime
seonghobae Sep 8, 2026
6fa9399
test(tool-capability): cover core temporal rejection paths
seonghobae Sep 8, 2026
0e1e286
test(tool-capability): separate scanner and isolation evidence policy
seonghobae Sep 8, 2026
5dbe5ef
fix(tool-capability): separate owner evidence policy pins
seonghobae Sep 8, 2026
afb2ab1
test(tool-capability): exercise separated owner policy pins
seonghobae Sep 8, 2026
0105988
test(tool-capability): pin runtime owner evidence profiles
seonghobae Sep 8, 2026
f01baa1
test(tool-capability): pin public boundary owner evidence
seonghobae Sep 8, 2026
a732126
test(tool-capability): pin activation owner evidence
seonghobae Sep 8, 2026
ce8fc4b
test(tool-capability): pin activation policy owner evidence
seonghobae Sep 8, 2026
0970faf
test(tool-capability): pin policy authority owner evidence
seonghobae Sep 8, 2026
cfa67ab
test(tool-capability): pin policy port owner evidence
seonghobae Sep 8, 2026
0493932
test(tool-capability): pin catalog drift owner evidence
seonghobae Sep 8, 2026
adb6ab4
test(tool-capability): pin cross-admission owner evidence
seonghobae Sep 8, 2026
d10c908
test(tool-capability): pin core window owner evidence
seonghobae Sep 8, 2026
48e79a9
test(tool-capability): propagate owner evidence fixtures
seonghobae Sep 8, 2026
281b595
test(tool-capability): align source-issued owner evidence
seonghobae Sep 8, 2026
276dac6
test(tool-capability): preserve core receipt rejection contract
seonghobae Sep 8, 2026
76af08c
test(tool-capability): cover owner evidence fail-closed branches
seonghobae Sep 8, 2026
b268b43
test(tool-capability): cover owner producer drift
seonghobae Sep 8, 2026
47c0b30
test(tool-capability): reject synthetic policy defaults
seonghobae Sep 8, 2026
0f974b7
fix(tool-capability): require explicit policy authority
seonghobae Sep 8, 2026
c0b2e44
docs(adr): require explicit extension policy evidence
seonghobae Sep 8, 2026
d07258a
test(tool-capability): align explicit policy authority
seonghobae Sep 8, 2026
74cfc6a
docs(changelog): require explicit policy authority
seonghobae Sep 8, 2026
b52f5f1
test(tool-capability): expose invocation owner-evidence TOCTOU
seonghobae Sep 8, 2026
fb0f1e9
fix(tool-capability): preserve owner evidence across invocation
seonghobae Sep 8, 2026
ef46c87
fix(tool-capability): snapshot validated invocation evidence
seonghobae Sep 8, 2026
404ad88
test(tool-capability): cover owner receipt identity drift
seonghobae Sep 8, 2026
e4cd6d0
fix(tool-capability): remove unreachable owner evidence route
seonghobae Sep 8, 2026
fe2e8d4
test(tool-capability): expose async publication authority drift
seonghobae Sep 8, 2026
c378630
fix(tool-capability): revalidate authority before invocation publication
seonghobae Sep 8, 2026
a37304c
test(tool-capability): cover publication policy drift
seonghobae Sep 8, 2026
3fdd0f8
test(tool-capability): bound invocation text bytes
seonghobae Sep 8, 2026
737f870
fix(tool-capability): bound invocation text bytes
seonghobae Sep 8, 2026
5aab7c0
test(tool-capability): cover core observed-content type rejection
seonghobae Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Noema remains independently deployable. CWL composition is through versioned pro

These are separate ownership domains. Noema does not duplicate their internal authority.

This revision adds a candidate Tool / Capability admission port at `src/tool-capability/external-extension-admission.ts` for external Claude community plugins. It is not an HTTP route and does not change `/health`, `/ready`, or `/exchange`. Marketplace discovery, Anthropic review, and plugin packaging are not runtime authority. The port stays a local fail-closed ACL until an immutable `context-graph-contracts` artifact contract exists.

## 5. Evidence and authority separation

| Plane | Meaning | Not equivalent to |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
- Tool / Capability Boundary에 Claude community plugin 외부 확장 승인 포트를 추가한다. 마켓플레이스 메타데이터, 가변 브랜치/태그, Anthropic 리뷰, 플러그인 지시문은 승인 권한이 아니다. exact commit/path/digest, AppGuardrail·격리 영수증, 제품/역할 범위, 만료·롤백, 중복 활성화 재현만 통과하고, 제품 런타임에서 플러그인 래퍼를 실행하지 않는다. `context-graph-contracts` 불변 계약이 나오기 전에는 로컬 포트와 테스트 더블만 쓴다. issue #545, ADR 0015.
- `noema-core` provider-neutral Shared Kernel을 추가하여 이미 해석된 PydanticAI `Model`과 역할별 prompt/schema만 받아 Agent를 구성한다. 문자열 model identifier와 provider discovery·credential·routing·retry·failover는 Shared Kernel 밖에 두고 `Agent(..., retries=0)`으로 repository-local model-attempt authority를 만들지 않는다. Reviewer wheel·sdist·editable 설치는 canonical `packages/noema-core` source를 포함하거나 참조하며 별도 100% coverage·docstring과 clean install smoke로 검증한다. 외부 소비는 immutable versioned publication·exact source identity·SBOM/provenance·licensing/NOTICE·compatibility/rollback evidence 전에는 허용하지 않는다.
- `writeAcquisitionPrivateFile`의 기존 대상 사전-교체 검증 read(`existingDescriptor` open)에 `O_NONBLOCK`을 추가해 fail-closed를 강화한다. 이 open은 이미 필수 filesystem capability로 `O_NONBLOCK`을 검증했지만 실제로는 사용하지 않아, 로컬 권한을 가진 행위자가 사전 `lstatSync` 정규 파일 확인과 이 open 사이에 대상 경로를 FIFO로 교체하면 writer가 나타날 때까지 무한정 블로킹해 writer lease를 계속 점유할 수 있었다. `O_NONBLOCK`은 정규 파일에는 영향이 없고, FIFO에서는 open이 즉시 반환되어 이어지는 descriptor 타입 검증이 그대로 fail-closed로 거부한다. 회귀 테스트(`test/acquisition-private-output-existing-target-nonblocking.test.ts`)와 기존 open-flags 계약 테스트 갱신으로 고정했다.
- `readStableFile`의 close-후 재검증 단계(`afterClosePath` lookup 실패)와 `writeAcquisitionPrivateFile`의 cleanup-시점 `O_NONBLOCK` 소실 분기에 대한 fail-closed 회귀 테스트를 추가해 `scripts/lib/acquisition-data-room-integrity.mjs`/`scripts/lib/acquisition-private-output.mjs`의 100% coverage 게이트를 복구한다. 동작 변화는 없다.
Expand Down
2 changes: 2 additions & 0 deletions docs/CONTEXT_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Protected `main` also includes the durable execution slice integrated through #5

Owns versioned allowlisted tool/capability descriptors, least-authority invocation, expiry, input/output bounds, and capability provenance. Arbitrary caller/model shell or network authority is not a Noema tool contract.

This revision adds a local fail-closed admission port for external Claude community plugins (`src/tool-capability/external-extension-admission.ts`, ADR 0015, issue #545). Marketplace metadata, Anthropic review, mutable branch/tag refs, and plugin instructions are not admission authority. Exact repository/commit/path/digest identity, independently pinned AppGuardrail and quarantine receipts, product/role scope, expiry/rollback, and idempotent activation/invocation receipts are. Product-runtime execution of a Claude plugin wrapper is rejected. Until `context-graph-contracts` publishes an immutable shared artifact contract, this port is a local ACL/test double rather than a released shared-kernel dependency. The slice is candidate truth on this revision until protected integration.

### State / Checkpoint

Owns versioned runtime checkpoint semantics needed for restart/cancellation/idempotency. Checkpoints contain only Noema runtime state and canonical foreign references; they must not copy another product's domain truth, provider credential state, or unrestricted reasoning/tool payloads.
Expand Down
2 changes: 1 addition & 1 deletion docs/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ The protected Workflow / Task foundation admits one canonical execution identity

Protected `main` also includes the durable Workflow / Task Execution slice integrated through #542: Durable Object state binding/routing, complete execution-plan authority, atomic task claim and checkpoint CAS/replay, effect-start and terminal evidence, cancellation/recovery authority, retained provenance, and hostile stored-record validation. This protected slice grants Noema runtime authority only under an explicit retained claim identity; it does not prove deployed Durable Object transaction compatibility, successful external side effects, or production runtime operation. ADR 0013 therefore remains `Proposed` until its deployment/runtime acceptance evidence exists.

`contextual-orchestrator` remains the sole model discovery and routing owner; Noema does not add direct provider SDKs, provider credentials, provider fallback lists, or local routing policy. Tool / Capability Boundary, Isolation Integration, Policy / Approval, Observability, and Recovery remain separate bounded contexts under ADR 0012 and the canonical Context Map. Context Graph/EA integration requires an immutable released `context-graph-contracts` contract/profile and preserves EA Core as the authoritative Decision Plane; cross-service SQL is forbidden.
`contextual-orchestrator` remains the sole model discovery and routing owner; Noema does not add direct provider SDKs, provider credentials, provider fallback lists, or local routing policy. Tool / Capability Boundary, Isolation Integration, Policy / Approval, Observability, and Recovery remain separate bounded contexts under ADR 0012 and the canonical Context Map. This revision adds a fail-closed Tool / Capability admission port for external Claude community plugins (issue #545, ADR 0015): marketplace metadata, Anthropic review, mutable refs, and plugin instructions are not admission authority, and product-runtime Claude plugin wrappers are rejected. Until `context-graph-contracts` publishes an immutable shared artifact contract, Noema keeps a local port and test double only. Context Graph/EA integration requires an immutable released `context-graph-contracts` contract/profile and preserves EA Core as the authoritative Decision Plane; cross-service SQL is forbidden.

## 5. Functional requirements

Expand Down
3 changes: 2 additions & 1 deletion docs/TEST_STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Coverage 대상과 제외는 `vitest.config.ts` 및 reviewer CI가 source of tru
- URL/origin/ref/SHA validators;
- review/check/status reduction logic;
- duplicate-key/UTF-8/path validators;
- KPI/evidence schema logic.
- KPI/evidence schema logic;
- Tool / Capability external-extension admission, activation, and invocation receipts.

핵심은 attacker-controlled input에 대한 closed-set acceptance입니다.

Expand Down
2 changes: 2 additions & 0 deletions docs/TRACEABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Each arrow is a separate authority. Success at an earlier stage cannot fabricate
| Patch-validator image supply chain | issue #66 + protected implementation | `Dockerfile.patch-validator`, image workflow, validator runtime/profile, SBOM/scanner/receipt validators | exact build/runtime/smoke/SBOM/vulnerability/receipt/final-head verification | protected-main operational receipt and later publication/signing/activation evidence | Source/runtime/supply-chain implementation is integrated on protected main; later operational/publication authority remains separate |
| Licensing/IP authority | licensing/IP contract | rights/evidence validators | duplicate-key/UTF-8/exact-artifact and rights-metadata tests | owner/legal grant and transfer evidence | Technical controls exist; legal authority external |
| Release/acquisition readiness | release/provenance/acquisition contracts | release verification and evidence scripts, digest-bound revenue/transfer source documents | exact-source package/SBOM/provenance/readiness and retained-source byte-integrity tests | immutable release/deployment/customer/revenue/legal authority | Technical byte binding implemented; commercial/legal authenticity remains external |
| External Claude plugin admission | ADR 0015 + issue #545 + FR-022 | `src/tool-capability/external-extension-admission.ts` local fail-closed port/ACL | `test/external-extension-admission.test.ts` covering mutable source, catalog mismatch, forged receipts, forbidden authority, cross-product activation, expiry/rollback, catalog drift, replay, instruction promotion, product-runtime wrappers, and secret/product/reasoning receipts | immutable `context-graph-contracts` artifact contract, AppGuardrail successor evidence, isolation/egress operation, measured pilots | Implemented on active PR / In review; protected-main maturity follows integration |

## 3. Live governance traceability

Expand All @@ -78,6 +79,7 @@ Historical or integrated PR numbers are deliberately omitted from current owners
| Patch-validator operational/publication proof | issue #66 | Source/image verification is integrated; protected-main operational receipt and later publication/signing/attestation/activation remain distinct authorities. |
| Authentic production KPI evidence | issue #3 | Requires real production-window data; repository fixtures or synthetic evidence cannot satisfy it. |
| Acquisition coordination | issue #5 | Coordinates evidence families without promoting earlier evidence into buyer/legal/commercial authority. |
| External Claude plugin admission | issue #545 | Local fail-closed Tool / Capability port only; marketplace installation, Anthropic review, isolation runtime, and shared-contract GA remain separate authorities. |

Canonical architecture/documentation is code-current by revision and is not owned by a historical documentation PR. Transient queue/green states belong to observation-scoped evidence, not timeless architecture claims.

Expand Down
4 changes: 4 additions & 0 deletions docs/TRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ src/runtime-entrypoint.ts

자세한 구현과 route ownership은 `ARCHITECTURE.md`, `docs/api-spec.md`를 따릅니다.

### 2.2 External Claude plugin admission

Tool / Capability Boundary의 로컬 포트 `src/tool-capability/external-extension-admission.ts`는 Claude community plugin 서술자를 exact repository/commit/path/digest와 독립적으로 pin된 AppGuardrail·격리 영수증에 결합한다. 가변 브랜치/태그, 로컬 경로, 마켓플레이스/카탈로그 불일치, 공급자 키, 광역 GitHub 권한, 미선언 셸/파일/네트워크/비밀/MCP, 다른 제품 승인, 만료·롤백, 카탈로그 drift, 관측 내용의 정책 승격, 제품 런타임 플러그인 래퍼는 실패-폐쇄한다. 이 포트는 HTTP API가 아니며 `/exchange` 권한을 바꾸지 않는다. `context-graph-contracts` 불변 계약이 나오기 전에는 로컬 ACL/테스트 더블이다.

### 2.1 `/exchange` inbound body deadline

`POST /exchange`의 JSON body는 UTF-8 wire bytes 기준 최대 **8,192 bytes**이고, body read가 시작된 뒤 전체 stream은 **10,000 ms의 절대 wall-clock deadline** 안에 완료되어야 합니다. 작은 chunk를 반복해서 보내더라도 deadline은 재설정되지 않습니다. 제한시간을 넘긴 incomplete stream은 best-effort로 취소하고 **HTTP 408**의 Noema 표준 JSON error envelope로 실패-폐쇄하며, 이 경계는 distributed rate-limit delegation, OIDC/JWKS 검증, GitHub App private-key 사용과 GitHub API 호출보다 앞에서 적용됩니다.
Expand Down
1 change: 1 addition & 0 deletions docs/UML.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ flowchart LR
READY[runtime readiness]
RATE[NoemaRateLimiter]
REPLAY[NoemaOidcReplayGuard]
TOOL[tool-capability admission]
end

subgraph ReviewPlane[Review and model plane]
Expand Down
52 changes: 52 additions & 0 deletions docs/adr/0015-external-extension-admission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# ADR 0015: Fail-closed admission for external Claude community plugins

Status: Proposed

## Context

CWL products can discover curated plugins through `anthropics/claude-plugins-community`, but wholesale marketplace installation would turn third-party prompts, hooks, MCP servers, shell commands, and network access into implicit runtime authority. Anthropic review of that catalog is useful upstream evidence, not CWL admission authority. Issue #545 assigns the Tool / Capability Boundary the job of versioned external-extension descriptors, least-authority activation, expiry, rollback, and invocation receipts.

`context-graph-contracts` does not yet publish an immutable shared artifact contract for this descriptor. AppGuardrail, quarantine-sandbox-runtime, EgressWeave, and Keyverse remain the owners of scanning, isolation, egress, and secret handles. Waiting for those foreign GA releases would stall an independently verifiable Noema port.

## Decision

Noema keeps a local fail-closed Tool / Capability port in `src/tool-capability/external-extension-admission.ts`:

- Admission binds exact `upstream_repository`, lowercase commit SHA, relative path, artifact digest, and marketplace-entry digest.
- Mutable branches, tags, `latest` versions, absolute paths, and parent-segment paths are rejected.
- Marketplace metadata that disagrees with an independently pinned catalog is rejected.
- AppGuardrail and quarantine receipts must be pinned separately and must match the artifact and isolation policy.
- `developer_assist` admits no filesystem, network, process, secret, or MCP capabilities. Provider keys and broad GitHub authority are forbidden.
- Product-scoped activation cannot use another product's approval. `approved_for_pilot` is not invocation authority.
- Expired, suspended, superseded, rejected, or rollback-marked extensions cannot be invoked.
- Catalog drift after admission cannot silently update an active extension.
- Duplicate activation and invocation events are idempotent replay; conflicting retained events fail closed.
- Plugin instructions cannot promote observed content into trusted policy or new capability.
- Product-runtime mode cannot execute a Claude plugin wrapper.
- Invocation receipts contain only identity fields and must not carry secrets, raw product data, or hidden reasoning.

This port is a test double and Anti-Corruption Layer until an immutable `context-graph-contracts` release exists. Noema does not copy plugin source, install the marketplace, or treat Anthropic review as CWL trust.

The decision follows least privilege and complete mediation (Saltzer & Schroeder, 1975) and fail-closed verification of untrusted software components (National Institute of Standards and Technology, 2022).

## Consequences

Operators can reject hostile plugin metadata deterministically without waiting for foreign GA. The cost is a local descriptor that must later be replaced by a released shared contract without changing the fail-closed invariants. AppGuardrail and quarantine receipts remain pins, not proof that those owners have completed their own product work.

## Rejected alternatives

- **Wholesale marketplace installation:** rejected because unreviewed connectors would inherit runtime authority.
- **Trust Anthropic catalog review as CWL admission:** rejected because upstream review is not this organization's authority.
- **Copy plugin source into Noema or product repositories:** rejected because it creates a mutable foreign system of record.
- **Wait for `context-graph-contracts` GA before any Noema port:** rejected because a local fail-closed ACL is independently verifiable and can later consume the released contract.
- **Allow product-runtime Claude plugin wrappers:** rejected because product execution must use product-owned protocol/API ports, not community plugin packaging.

## Acceptance

This ADR remains `Proposed` until the local port is protected source, exact-head CI/security/review evidence is terminal clean, and later slices bind trusted research/execution producers, immutable shared-contract consumption, AppGuardrail successor evidence, and measured pilot activation. Source tests do not prove live plugin installation, isolation runtime operation, or buyer completion of issue #545.

## References

National Institute of Standards and Technology. (2022). *Secure software development framework (SSDF) version 1.1: Recommendations for mitigating the risk of software vulnerabilities* (NIST Special Publication 800-218). https://doi.org/10.6028/NIST.SP.800-218

Saltzer, J. H., & Schroeder, M. D. (1975). The protection of information in computer systems. *Proceedings of the IEEE, 63*(9), 1278–1308. https://doi.org/10.1109/PROC.1975.9939
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ADR은 **왜 이 구조를 선택했는지**를 기록합니다. 구현 상태
| [0012](./0012-runtime-orchestration-bounded-contexts.md) | Proposed | Agent Runtime, Workflow / Task Execution, Tool / Capability, State / Checkpoint, isolation, policy, observability, recovery의 소유권을 분리하고 provider routing·foreign truth·cross-service SQL을 Noema 경계 밖에 둔다. |
| [0013](./0013-durable-workflow-execution-authority.md) | Proposed | runnable candidate와 durable claim/effect start/terminal recovery/checkpoint commit을 분리하고 bounded transition provenance를 Noema state-store 경계에 둔다. |
| [0014](./0014-shared-noema-core-package.md) | Proposed | role-neutral PydanticAI `Agent(...)` construction만 `packages/noema-core` Shared Kernel로 추출하고 provider routing·credential policy·verdict·tool/deps·tenant truth는 canonical owner에 남긴다. |
| [0015](./0015-external-extension-admission.md) | Proposed | Claude community plugin은 exact commit/path/digest와 별도 scan 영수증으로만 승인하고, 마켓플레이스 설치·Anthropic 리뷰·제품 런타임 래퍼는 실패-폐쇄한다. |

## ADR lifecycle

Expand Down
2 changes: 2 additions & 0 deletions docs/threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
6. Cloudflare가 허용하는 대용량 또는 chunked JSON request를 이용한 isolate 메모리·CPU 고갈
7. GitHub OIDC/JWKS 또는 GitHub App API subrequest가 응답하지 않아 `/exchange` 요청과 Worker 자원을 장시간 점유하는 가용성 저하
8. 신뢰된 GitHub endpoint가 과대 또는 길이 미상 response body를 반환해 `response.json()` 이전에 isolate 메모리를 고갈시키는 가용성 저하
9. Claude community plugin 마켓플레이스 메타데이터, Anthropic 리뷰, 가변 브랜치/태그, 또는 플러그인 지시문을 런타임 권한으로 승격하려는 시도

## 대응
- `iss`, `aud`, `repository_owner`, `workflow_ref` 엄격 검증
Expand Down Expand Up @@ -47,6 +48,7 @@
- 유출 의심 시 즉시 비밀키 폐기 후 신규 발급
- 대상 조직 권한 재검토
- 로그에서 `Authorization`, `token`, `pem`, JSON request body 제거
- Claude community plugin은 exact commit/path/digest와 별도 AppGuardrail·격리 영수증으로만 승인하고, 마켓플레이스 설치·제품 런타임 래퍼·비밀/제품 데이터 영수증은 실패-폐쇄함 (ADR 0015)

## 참고
- Cloudflare Workers limits: https://developers.cloudflare.com/workers/platform/limits/
Expand Down
Loading
Loading