Skip to content

Commit ae7fbcd

Browse files
committed
feat: add premium entitlement and extension contract
1 parent eb9bf7c commit ae7fbcd

5 files changed

Lines changed: 279 additions & 2 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://github.com/RMANOV/sqlite-memory-mcp/docs/premium/entitlement.schema.json",
4+
"title": "sqlite-memory premium entitlement",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"required": [
8+
"entitlement_id",
9+
"customer_id",
10+
"plan",
11+
"features",
12+
"signature"
13+
],
14+
"properties": {
15+
"entitlement_id": {
16+
"type": "string",
17+
"minLength": 1
18+
},
19+
"customer_id": {
20+
"type": "string",
21+
"minLength": 1
22+
},
23+
"plan": {
24+
"type": "string",
25+
"minLength": 1
26+
},
27+
"features": {
28+
"type": "array",
29+
"minItems": 1,
30+
"items": {
31+
"type": "string",
32+
"minLength": 1
33+
}
34+
},
35+
"machine_ids": {
36+
"type": "array",
37+
"items": {
38+
"type": "string",
39+
"minLength": 1
40+
},
41+
"default": []
42+
},
43+
"org_id": {
44+
"type": "string"
45+
},
46+
"seat_count": {
47+
"type": "integer",
48+
"minimum": 1
49+
},
50+
"issued_at": {
51+
"type": "string",
52+
"format": "date-time"
53+
},
54+
"not_before": {
55+
"type": "string",
56+
"format": "date-time"
57+
},
58+
"expires_at": {
59+
"type": "string",
60+
"format": "date-time"
61+
},
62+
"owner_approval_sha256": {
63+
"type": "string",
64+
"pattern": "^sha256:[0-9a-f]{64}$"
65+
},
66+
"metadata": {
67+
"type": "object",
68+
"additionalProperties": true
69+
},
70+
"signature": {
71+
"type": "object",
72+
"additionalProperties": false,
73+
"required": [
74+
"alg",
75+
"value"
76+
],
77+
"properties": {
78+
"alg": {
79+
"type": "string",
80+
"const": "ed25519"
81+
},
82+
"value": {
83+
"type": "string",
84+
"minLength": 1,
85+
"description": "Base64-encoded Ed25519 signature over the canonical JSON payload excluding this signature object."
86+
},
87+
"key_id": {
88+
"type": "string"
89+
}
90+
}
91+
}
92+
}
93+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Private Premium Extension Contract
2+
3+
This file defines the minimal integration contract for a separate private repo.
4+
5+
## Goal
6+
7+
The public repo stays OSS and safe to share.
8+
The private repo contains premium logic and is loaded only through the gated runtime boundary.
9+
10+
## Required public-side contract
11+
12+
- Public loader: `premium_runtime.py`
13+
- Public contract module: `premium_contract.py`
14+
- Runtime env var for the private entrypoint:
15+
- `SQLITE_MEMORY_PREMIUM_ENTRYPOINT`
16+
17+
## Supported private entrypoint shapes
18+
19+
- `package.module`
20+
- `package.module:register`
21+
- `C:\path\to\premium_plugin.py`
22+
- `C:\path\to\premium_plugin.py::register`
23+
24+
## Recommended private function signature
25+
26+
```python
27+
from premium_contract import PremiumMountContext, PremiumRegistrationResult
28+
29+
def register_premium_extensions(
30+
mcp,
31+
*,
32+
server_name: str | None = None,
33+
mount_context: PremiumMountContext | None = None,
34+
) -> PremiumRegistrationResult:
35+
...
36+
```
37+
38+
## Guaranteed inputs from the public runtime
39+
40+
- `server_name`
41+
- `mount_context.contract_version`
42+
- `mount_context.feature_id`
43+
- `mount_context.machine_id`
44+
- `mount_context.config`
45+
46+
## Minimal private repo skeleton
47+
48+
```python
49+
from premium_contract import PREMIUM_RUNTIME_CONTRACT_VERSION
50+
51+
def register_premium_extensions(mcp, *, server_name=None, mount_context=None):
52+
if mount_context is None:
53+
raise RuntimeError("mount_context required")
54+
if mount_context.contract_version != PREMIUM_RUNTIME_CONTRACT_VERSION:
55+
raise RuntimeError("contract mismatch")
56+
57+
# mount premium tools or sub-MCP here
58+
return {
59+
"mounted": True,
60+
"contract_version": mount_context.contract_version,
61+
"extension_name": "sqlite-memory-premium",
62+
"features": ["acl_rbac", "partner_digest"],
63+
}
64+
```
65+
66+
## Boundary rule
67+
68+
The private repo must not assume direct access to secrets from the public repo.
69+
Entitlements, approval tokens, and public-key material stay outside git and are injected at runtime.

premium_contract.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Public contract for separate premium extension repositories.
2+
3+
This module is safe to publish. It defines the stable registration surface
4+
that a private premium repo can target without living inside the OSS core.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from dataclasses import dataclass, field
10+
from typing import Any, Protocol, TypedDict
11+
12+
PREMIUM_RUNTIME_CONTRACT_VERSION = "1.0"
13+
14+
15+
@dataclass(slots=True)
16+
class PremiumMountContext:
17+
"""Context passed from OSS core into a private premium extension."""
18+
19+
contract_version: str
20+
server_name: str
21+
feature_id: str
22+
machine_id: str
23+
config: dict[str, Any] = field(default_factory=dict)
24+
25+
26+
class PremiumRegistrationResult(TypedDict, total=False):
27+
"""Structured result returned by premium registration hooks."""
28+
29+
mounted: bool
30+
contract_version: str
31+
extension_name: str
32+
features: list[str]
33+
notes: str
34+
35+
36+
class PremiumExtensionRegistrar(Protocol):
37+
"""Callable signature for a private premium registration hook."""
38+
39+
def __call__(
40+
self,
41+
mcp: Any,
42+
*,
43+
server_name: str | None = None,
44+
mount_context: PremiumMountContext | None = None,
45+
) -> PremiumRegistrationResult | dict[str, Any] | None: ...
46+
47+
48+
def build_mount_context(
49+
*,
50+
server_name: str,
51+
feature_id: str,
52+
machine_id: str,
53+
config: dict[str, Any] | None = None,
54+
) -> PremiumMountContext:
55+
"""Build a standard context object for private premium extensions."""
56+
return PremiumMountContext(
57+
contract_version=PREMIUM_RUNTIME_CONTRACT_VERSION,
58+
server_name=server_name,
59+
feature_id=feature_id,
60+
machine_id=machine_id,
61+
config=dict(config or {}),
62+
)

premium_runtime.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
record_memory_event,
3131
setup_logger,
3232
)
33+
from premium_contract import build_mount_context
3334

3435
logger = setup_logger("sqlite-premium", "premium_runtime.log")
3536

@@ -738,6 +739,12 @@ def _resolve_module_from_entrypoint(entrypoint: str):
738739
def _register_loaded_module(
739740
module: Any, mcp: Any, server_name: str, attr_name: str | None
740741
):
742+
mount_context = build_mount_context(
743+
server_name=server_name,
744+
feature_id="private_extension_runtime",
745+
machine_id=MACHINE_ID,
746+
config=load_premium_config(),
747+
)
741748
if attr_name:
742749
target = getattr(module, attr_name, None)
743750
if target is None:
@@ -758,9 +765,16 @@ def _register_loaded_module(
758765

759766
if callable(target):
760767
try:
761-
return target(mcp, server_name=server_name)
768+
return target(
769+
mcp,
770+
server_name=server_name,
771+
mount_context=mount_context,
772+
)
762773
except TypeError:
763-
return target(mcp)
774+
try:
775+
return target(mcp, server_name=server_name)
776+
except TypeError:
777+
return target(mcp)
764778
if hasattr(mcp, "mount"):
765779
mcp.mount(target)
766780
return {"mounted": True}

tests/test_premium_runtime.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
77

88
import premium_runtime
9+
from premium_contract import PREMIUM_RUNTIME_CONTRACT_VERSION
910
from schema import init_db
1011

1112

@@ -217,3 +218,41 @@ def test_maybe_mount_premium_extensions_loads_private_module_when_allowed(
217218

218219
assert audit["decision"] == "load_success"
219220
assert audit["reason"] == "premium_extensions_loaded"
221+
222+
223+
def test_maybe_mount_premium_extensions_passes_mount_context(tmp_path, monkeypatch):
224+
db_path = str(tmp_path / "memory.db")
225+
init_db(db_path)
226+
premium_file = tmp_path / "premium_plugin_ctx.py"
227+
premium_file.write_text(
228+
"def register_premium_extensions(mcp, *, server_name=None, mount_context=None):\n"
229+
" mcp.loaded_server_name = server_name\n"
230+
" mcp.contract_version = mount_context.contract_version\n"
231+
" mcp.feature_id = mount_context.feature_id\n"
232+
" return {'mounted': True, 'contract_version': mount_context.contract_version}\n",
233+
encoding="utf-8",
234+
)
235+
mcp = DummyMCP()
236+
monkeypatch.setenv("SQLITE_MEMORY_PREMIUM_ENTRYPOINT", str(premium_file))
237+
monkeypatch.setattr(premium_runtime, "_get_conn", lambda: _conn_ctx(db_path))
238+
monkeypatch.setattr(
239+
premium_runtime,
240+
"evaluate_feature_gate",
241+
lambda conn, **kwargs: {
242+
"allowed": True,
243+
"decision": "allowed",
244+
"reason": "entitlement_valid",
245+
"feature_id": "private_extension_runtime",
246+
"entitlement_id": "ent-ctx",
247+
"customer_id": "cust-ctx",
248+
},
249+
)
250+
251+
result = premium_runtime.maybe_mount_premium_extensions(
252+
mcp, server_name="sqlite-unified"
253+
)
254+
255+
assert result["status"] == "loaded"
256+
assert mcp.loaded_server_name == "sqlite-unified"
257+
assert mcp.contract_version == PREMIUM_RUNTIME_CONTRACT_VERSION
258+
assert mcp.feature_id == "private_extension_runtime"

0 commit comments

Comments
 (0)