Skip to content

Commit 0ab88c0

Browse files
Gate 6 registry access policy
Derive organization BTD posture from registry ownership and read-license rows, require owner-read or licensed-read evidence for MCP and ChatGPT App write admission, and cover the registry access-policy templates in BTD tests. Keep Terminal-facing copy on registry, range, ownership, license, and proof posture without price-appreciation, dividend, copyright-transfer, or marketplace-royalty framing.
1 parent 4c70c2b commit 0ab88c0

26 files changed

Lines changed: 1302 additions & 55 deletions

BITCODE_SPEC_V28.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,14 @@ Acceptance criteria:
675675
- policy templates cover owner-read, licensed-read, derivative use, redistribution, confidentiality, dispute, and takedown posture.
676676
- Terminal copy avoids price-appreciation, dividend, copyright-transfer, or marketplace-royalty promises.
677677

678+
Implementation posture:
679+
680+
- Organization `$BTD` posture is a registry projection. Read models must collect member wallet bindings, then read ownership events and read-license rows from the BTD registry. Aggregate compatibility balances may remain contextual but must not admit organization, MCP, ChatGPT App, or read-license actions.
681+
- MCP admission may require current owner-read or licensed-read evidence for one or more AssetPacks. Deprecated aggregate holding gates must fail closed and point callers to registry-derived read-access requirements.
682+
- ChatGPT App write-capable connected-interface tools must require explicit user confirmation and typed read-access evidence carrying AssetPack id, wallet id, owner-read or licensed-read decision, policy hash, and reason.
683+
- Access-policy templates must remain source-readable and test-covered for owner-read, licensed-read, derivative use, redistribution, confidentiality, dispute, and takedown posture.
684+
- Active Terminal and Auxillaries copy must describe registry, range, ownership, license, and proof posture without implying price appreciation, dividends, copyright transfer, or marketplace royalties.
685+
678686
### Gate 7: Terminal Operations And Testnet Readiness
679687

680688
Purpose:

internal-docs/BITCODE_CHATGPT_APP_INTERFACE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,20 @@ It must not:
1919
- own Exchange state,
2020
- bypass Terminal/Exchange proof requirements,
2121
- write unconfirmed connected-interface changes,
22+
- write connected-interface changes without owner-read or licensed-read registry evidence for the relevant AssetPack,
2223
- use generic work-board examples as Bitcode product semantics.
2324

25+
## Gate 6 Read-Access Admission
26+
27+
Write-capable ChatGPT App tools require two independent admission facts:
28+
29+
- `confirmed: true` from the user interaction.
30+
- `readAccess` evidence with AssetPack id, wallet id, decision, access-policy hash, and reason.
31+
32+
The only admitted write decisions are `owner_read` and `licensed_read`. A balance,
33+
organization total, or generic account role is not enough to write through a
34+
connected interface.
35+
2436
## Requirements For SPEC Promotion
2537

2638
- tool names must be Bitcode-specific,

internal-docs/BITCODE_CONNECTED_SERVICES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Examples:
2222
- Source providers bind evidence and repository scope.
2323
- Delivery providers receive AssetPacks or AssetPack partials.
2424
- Every write-capable connected interface must fail closed without readiness and admission receipts.
25+
- MCP and ChatGPT App writes must admit against owner-read or licensed-read registry evidence, not aggregate `$BTD` holding thresholds.
26+
- Organization `$BTD` posture must read member wallet bindings through BTD registry ownership and read-license rows.
2527
- Provider payloads must normalize toward Read, fit, AssetPack, settlement, proof, or delivery-mechanism semantics.
2628

2729
## Current Source Examples

packages/btd/__tests__/btd.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import {
99
BtdFungibleMutationRejectedError,
1010
BTD_MAX_MINTABLE_SUPPLY,
1111
BITCODE_FEE_ASSET,
12+
assertBtdAccessPolicyTemplateCoverage,
1213
assertBtdMintableSupplyLimit,
14+
buildBtdReadAccessProjectionFromRegistryRows,
1315
calculateLlmBtcFeeEstimate,
1416
calculateMeasuredBtdFromTokens,
17+
evaluateBtdReadAccess,
18+
listBtdAccessPolicyTemplates,
1519
} from '../src';
1620

1721
describe('calculateLlmBtcFeeEstimate', () => {
@@ -63,3 +67,101 @@ describe('BtdFungibleMutationRejectedError', () => {
6367
expect(err.code).toBe('BTD_IS_NON_FUNGIBLE');
6468
});
6569
});
70+
71+
describe('BTD access policy templates', () => {
72+
it('covers owner-read, licensed-read, policy, dispute, and takedown posture', () => {
73+
const templates = assertBtdAccessPolicyTemplateCoverage(listBtdAccessPolicyTemplates());
74+
75+
expect(templates.map((template) => template.kind)).toEqual(
76+
expect.arrayContaining([
77+
'owner_read',
78+
'licensed_read',
79+
'derivative_use',
80+
'redistribution',
81+
'confidentiality',
82+
'dispute',
83+
'takedown',
84+
]),
85+
);
86+
expect(templates.flatMap((template) => template.prohibitedClaims)).toEqual(
87+
expect.arrayContaining([
88+
'price appreciation',
89+
'dividend',
90+
'copyright transfer',
91+
'marketplace royalty',
92+
]),
93+
);
94+
});
95+
});
96+
97+
describe('registry-derived read access projection', () => {
98+
it('maps range, ownership, and read-license rows into owner-read decisions', () => {
99+
const projection = buildBtdReadAccessProjectionFromRegistryRows({
100+
assetPackId: 'asset-pack-1',
101+
range: {
102+
asset_pack_id: 'asset-pack-1',
103+
range_start: 10,
104+
range_end_exclusive: 15,
105+
token_count: 5,
106+
access_policy_id: 'policy-1',
107+
access_policy_hash: 'policy-hash',
108+
},
109+
ownershipRows: [
110+
{
111+
to_wallet_id: 'wallet-owner',
112+
asset_pack_id: 'asset-pack-1',
113+
range_start: 10,
114+
range_end_exclusive: 15,
115+
access_policy_hash: 'policy-hash',
116+
},
117+
],
118+
});
119+
120+
const decision = evaluateBtdReadAccess({
121+
walletId: 'wallet-owner',
122+
assetPackId: 'asset-pack-1',
123+
accessPolicy: projection.accessPolicy,
124+
ownershipClaims: projection.ownershipClaims,
125+
licenses: projection.licenses,
126+
at: '2026-05-19T00:00:00.000Z',
127+
});
128+
129+
expect(projection.range.tokenCount).toBe(5);
130+
expect(decision.decision).toBe('owner_read');
131+
});
132+
133+
it('maps registry read-license rows into licensed-read decisions', () => {
134+
const projection = buildBtdReadAccessProjectionFromRegistryRows({
135+
assetPackId: 'asset-pack-1',
136+
range: {
137+
asset_pack_id: 'asset-pack-1',
138+
range_start: 10,
139+
range_end_exclusive: 15,
140+
token_count: 5,
141+
access_policy_id: 'policy-1',
142+
access_policy_hash: 'policy-hash',
143+
},
144+
licenseRows: [
145+
{
146+
license_id: 'license-1',
147+
wallet_id: 'wallet-reader',
148+
asset_pack_id: 'asset-pack-1',
149+
access_policy_hash: 'policy-hash',
150+
valid_from: '2026-05-01T00:00:00.000Z',
151+
expires_at: '2026-06-01T00:00:00.000Z',
152+
},
153+
],
154+
});
155+
156+
const decision = evaluateBtdReadAccess({
157+
walletId: 'wallet-reader',
158+
assetPackId: 'asset-pack-1',
159+
accessPolicy: projection.accessPolicy,
160+
ownershipClaims: projection.ownershipClaims,
161+
licenses: projection.licenses,
162+
at: '2026-05-19T00:00:00.000Z',
163+
});
164+
165+
expect(decision.decision).toBe('licensed_read');
166+
});
167+
});

packages/btd/__tests__/v27-crypto-primitives.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ describe('V27 Terminal journal and ledger/database reconciliation primitives', (
15121512
metaphysicalFacts: [
15131513
{
15141514
factId: 'public-private-fact',
1515-
factKind: 'read_fit_context',
1515+
factKind: 'need_fit_context',
15161516
canonicalRoot: 'root',
15171517
private: false,
15181518
},

packages/btd/jest.config.cjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
rootDir: '.',
5+
testMatch: ['<rootDir>/__tests__/**/*.test.ts'],
6+
transform: {
7+
'^.+\\.tsx?$': [
8+
'ts-jest',
9+
{
10+
tsconfig: '<rootDir>/../../tsconfig.json',
11+
diagnostics: false,
12+
},
13+
],
14+
},
15+
};

packages/btd/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@
44
"private": true,
55
"main": "src/index.ts",
66
"types": "src/index.ts",
7+
"scripts": {
8+
"test": "jest --passWithNoTests",
9+
"typecheck": "tsc --noEmit --pretty false"
10+
},
711
"dependencies": {
812
"@bitcode/models": "workspace:*",
913
"@bitcode/supabase": "workspace:*"
14+
},
15+
"devDependencies": {
16+
"@types/jest": "^29.5.12",
17+
"@types/node": "^20.0.0",
18+
"jest": "^29.7.0",
19+
"ts-jest": "^29.1.1",
20+
"typescript": "^5.0.0"
1021
}
1122
}

0 commit comments

Comments
 (0)