stigmem-plugin-source-attestation 0.1.0__tar.gz

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.
@@ -0,0 +1,70 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ .eggs/
7
+ dist/
8
+ build/
9
+ .venv/
10
+ venv/
11
+ .uv/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .pytest_cache/
15
+ htmlcov/
16
+ .coverage
17
+ coverage.xml
18
+ *.cover
19
+ coverage/
20
+
21
+ # Node / pnpm
22
+ node_modules/
23
+ .next/
24
+ .turbo/
25
+ .jscpd/
26
+ .pnpm-store/
27
+ dist/
28
+ *.tsbuildinfo
29
+ apps/dashboard/coverage/
30
+ adapters/mcp/coverage/
31
+ sdks/stigmem-ts/coverage/
32
+
33
+ # Environment
34
+ .env
35
+ .env.local
36
+ .env.*.local
37
+
38
+ # Release signing keys — public keys are attached to GitHub Releases only;
39
+ # private keys must NEVER be committed (use offline storage).
40
+ stigmem-release-signing-key.asc
41
+ stigmem-release-signing-key*.asc
42
+ *private*signing*key*.asc
43
+ *secret*signing*key*.asc
44
+
45
+ # IDE
46
+ .vscode/
47
+ .idea/
48
+ *.swp
49
+ *.swo
50
+
51
+ # OS
52
+ .DS_Store
53
+ Thumbs.db
54
+
55
+ # Local Codex project instructions
56
+ AGENTS.md
57
+
58
+ # Docker
59
+ *.log
60
+
61
+ # Stigmem runtime (DB and logs are runtime state, not source)
62
+ data/*.db
63
+ data/*.db-shm
64
+ data/*.db-wal
65
+ stigmem.db
66
+ stigmem.db-shm
67
+ stigmem.db-wal
68
+ logs/
69
+ # Eval results (CI-generated artifacts)
70
+ eval/results/
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: stigmem-plugin-source-attestation
3
+ Version: 0.1.0
4
+ Summary: Experimental source-attestation plugin for Stigmem.
5
+ Project-URL: Homepage, https://github.com/eidetic-labs/stigmem
6
+ Project-URL: Documentation, https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation
7
+ Project-URL: Repository, https://github.com/eidetic-labs/stigmem
8
+ Project-URL: Issues, https://github.com/eidetic-labs/stigmem/issues
9
+ Author-email: Eidetic Labs <oss@eidetic-labs.ai>
10
+ License: Apache-2.0
11
+ Keywords: federation,plugins,provenance,source-attestation,stigmem
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: pydantic<3,>=2
21
+ Requires-Dist: stigmem-node<1.0.0,>=0.9.0a8
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Stigmem Source Attestation Plugin
25
+
26
+ Experimental source attestation plugin for Stigmem.
27
+
28
+ This package provides the `stigmem-plugin-source-attestation` source package
29
+ for alpha validation. It registers through the `stigmem.plugins` entry point
30
+ group and is loaded by `stigmem-node` only when explicitly installed and
31
+ configured by an operator.
32
+
33
+ ## Status
34
+
35
+ Source attestation remains experimental. Installing this package does not add
36
+ assertion-source enforcement, recall source weighting, or federation source
37
+ guards to the supported default surface. Default installs remain inert unless
38
+ the plugin is registered and the operator enables the relevant gates.
39
+
40
+ The package metadata is publication-shaped for the plugin readiness track, but
41
+ registry publication remains on hold until dry-run evidence and maintainer
42
+ clearance are recorded. See the feature record under
43
+ `features/source-attestation/` for the current status, evidence, and security
44
+ notes.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install --pre stigmem-node==0.9.0a8 stigmem-plugin-source-attestation==0.1.0
50
+ ```
51
+
52
+ ## Enable
53
+
54
+ Set the plugin gate environment variable to opt in:
55
+
56
+ ```bash
57
+ export STIGMEM_SOURCE_ATTESTATION_ENABLED=1
58
+ ```
59
+
60
+ The default install is inert; source attestation only activates when the
61
+ package is installed, discovered through the `stigmem.plugins` entry point, and
62
+ the operator enables the gate. Enforcement-specific gates such as
63
+ `STIGMEM_SOURCE_ATTESTATION_ENFORCE_ASSERT_VALIDATION` and
64
+ `STIGMEM_SOURCE_ATTESTATION_ENFORCE_FEDERATION_INBOUND` remain opt-in and must
65
+ not be enabled with warn-only mode.
66
+
67
+ ## Disable
68
+
69
+ Unset the plugin gate environment variable, or set it to any value other than
70
+ `1`, `true`, `yes`, or `on`:
71
+
72
+ ```bash
73
+ unset STIGMEM_SOURCE_ATTESTATION_ENABLED
74
+ ```
75
+
76
+ The plugin returns to inert state at the next process start. No data migration
77
+ is required; core source, scope, tenant, and audit enforcement continues to hold
78
+ without plugin participation.
79
+
80
+ ## Test
81
+
82
+ From a Stigmem repository checkout with development dependencies installed:
83
+
84
+ ```bash
85
+ uv run pytest node/tests/plugins/test_source_attestation_plugin_scaffold.py \
86
+ node/tests/plugins/test_source_attestation_plugin_validation.py
87
+ ```
88
+
89
+ The package itself ships no separate test tree; upstream plugin validation
90
+ lives in `node/tests/plugins/`.
91
+
92
+ ## Uninstall
93
+
94
+ ```bash
95
+ pip uninstall stigmem-plugin-source-attestation
96
+ ```
97
+
98
+ Removing the package is sufficient. The gate environment variable becomes moot
99
+ once the entry point is no longer discoverable.
100
+
101
+ ## Project Links
102
+
103
+ - Repository: <https://github.com/eidetic-labs/stigmem>
104
+ - Feature record: <https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation>
105
+ - Plugin source: <https://github.com/eidetic-labs/stigmem/tree/main/experimental/source-attestation>
106
+ - Issue tracker: <https://github.com/eidetic-labs/stigmem/issues>
@@ -0,0 +1,83 @@
1
+ # Stigmem Source Attestation Plugin
2
+
3
+ Experimental source attestation plugin for Stigmem.
4
+
5
+ This package provides the `stigmem-plugin-source-attestation` source package
6
+ for alpha validation. It registers through the `stigmem.plugins` entry point
7
+ group and is loaded by `stigmem-node` only when explicitly installed and
8
+ configured by an operator.
9
+
10
+ ## Status
11
+
12
+ Source attestation remains experimental. Installing this package does not add
13
+ assertion-source enforcement, recall source weighting, or federation source
14
+ guards to the supported default surface. Default installs remain inert unless
15
+ the plugin is registered and the operator enables the relevant gates.
16
+
17
+ The package metadata is publication-shaped for the plugin readiness track, but
18
+ registry publication remains on hold until dry-run evidence and maintainer
19
+ clearance are recorded. See the feature record under
20
+ `features/source-attestation/` for the current status, evidence, and security
21
+ notes.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install --pre stigmem-node==0.9.0a8 stigmem-plugin-source-attestation==0.1.0
27
+ ```
28
+
29
+ ## Enable
30
+
31
+ Set the plugin gate environment variable to opt in:
32
+
33
+ ```bash
34
+ export STIGMEM_SOURCE_ATTESTATION_ENABLED=1
35
+ ```
36
+
37
+ The default install is inert; source attestation only activates when the
38
+ package is installed, discovered through the `stigmem.plugins` entry point, and
39
+ the operator enables the gate. Enforcement-specific gates such as
40
+ `STIGMEM_SOURCE_ATTESTATION_ENFORCE_ASSERT_VALIDATION` and
41
+ `STIGMEM_SOURCE_ATTESTATION_ENFORCE_FEDERATION_INBOUND` remain opt-in and must
42
+ not be enabled with warn-only mode.
43
+
44
+ ## Disable
45
+
46
+ Unset the plugin gate environment variable, or set it to any value other than
47
+ `1`, `true`, `yes`, or `on`:
48
+
49
+ ```bash
50
+ unset STIGMEM_SOURCE_ATTESTATION_ENABLED
51
+ ```
52
+
53
+ The plugin returns to inert state at the next process start. No data migration
54
+ is required; core source, scope, tenant, and audit enforcement continues to hold
55
+ without plugin participation.
56
+
57
+ ## Test
58
+
59
+ From a Stigmem repository checkout with development dependencies installed:
60
+
61
+ ```bash
62
+ uv run pytest node/tests/plugins/test_source_attestation_plugin_scaffold.py \
63
+ node/tests/plugins/test_source_attestation_plugin_validation.py
64
+ ```
65
+
66
+ The package itself ships no separate test tree; upstream plugin validation
67
+ lives in `node/tests/plugins/`.
68
+
69
+ ## Uninstall
70
+
71
+ ```bash
72
+ pip uninstall stigmem-plugin-source-attestation
73
+ ```
74
+
75
+ Removing the package is sufficient. The gate environment variable becomes moot
76
+ once the entry point is no longer discoverable.
77
+
78
+ ## Project Links
79
+
80
+ - Repository: <https://github.com/eidetic-labs/stigmem>
81
+ - Feature record: <https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation>
82
+ - Plugin source: <https://github.com/eidetic-labs/stigmem/tree/main/experimental/source-attestation>
83
+ - Issue tracker: <https://github.com/eidetic-labs/stigmem/issues>
@@ -0,0 +1,18 @@
1
+ # Spec-X6-Source-Attestation Status
2
+
3
+ This file is a compatibility pointer for existing
4
+ `experimental/source-attestation/` links.
5
+
6
+ The canonical ADR-020 status record now lives at
7
+ [`features/source-attestation/status.md`](../../features/source-attestation/status.md).
8
+
9
+ Current summary:
10
+
11
+ - Status: `active`
12
+ - Stability: `experimental`
13
+ - Default surface: `opt-in`
14
+ - Implementation path: `experimental/source-attestation/`
15
+ - Package: `stigmem-plugin-source-attestation`
16
+
17
+ The implementation package remains here during transition. Product status,
18
+ gates, history, and release-facing facts belong in the feature record.
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: Source Attestation
3
+ sidebar_label: Source Attestation
4
+ audience: Integrator
5
+ ---
6
+
7
+ # Source Attestation
8
+
9
+ **Audience:** Node operators enforcing provenance guarantees on fact assertions.
10
+
11
+ :::info Coming soon
12
+ This guide covers Source Attestation , a the pre-reset spec addition. Spec draft is in progress.
13
+ :::
14
+
15
+ Source attestation binds a fact's `source` field to the caller's registered `entity_uri`. The node verifies that the asserted `source` matches the API key's registered identity.
16
+
17
+ Three modes:
18
+ - `enforce` — reject mismatched source claims with HTTP 403
19
+ - `warn` — accept and log; sets `attested: false` on the fact
20
+ - `off` — no verification (default in the pre-reset spec)
21
+
22
+ When shipped, this guide will cover configuration, key registration, and audit log integration.
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "stigmem-plugin-source-attestation"
3
+ version = "0.1.0"
4
+ description = "Experimental source-attestation plugin for Stigmem."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = { text = "Apache-2.0" }
8
+ authors = [
9
+ { name = "Eidetic Labs", email = "oss@eidetic-labs.ai" },
10
+ ]
11
+ keywords = ["stigmem", "plugins", "source-attestation", "provenance", "federation"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ ]
21
+ dependencies = [
22
+ "pydantic>=2,<3",
23
+ "stigmem-node>=0.9.0a8,<1.0.0",
24
+ ]
25
+
26
+ [project.entry-points."stigmem.plugins"]
27
+ source-attestation = "stigmem_plugin_source_attestation:plugin_manifest"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/eidetic-labs/stigmem"
31
+ Documentation = "https://github.com/eidetic-labs/stigmem/tree/main/features/source-attestation"
32
+ Repository = "https://github.com/eidetic-labs/stigmem"
33
+ Issues = "https://github.com/eidetic-labs/stigmem/issues"
34
+
35
+ [build-system]
36
+ requires = ["hatchling"]
37
+ build-backend = "hatchling.build"
38
+
39
+ [tool.hatch.build.targets.wheel]
40
+ packages = ["src/stigmem_plugin_source_attestation"]
41
+
42
+ [tool.hatch.build.targets.wheel.sources]
43
+ "src" = ""
44
+
45
+ [tool.hatch.build.targets.sdist]
46
+ include = [
47
+ "README.md",
48
+ "STATUS.md",
49
+ "security.md",
50
+ "spec.md",
51
+ "concept.md",
52
+ "src/stigmem_plugin_source_attestation/**/*.py",
53
+ ]
@@ -0,0 +1,23 @@
1
+ ---
2
+ feature: source-attestation
3
+ spec_id: Spec-X6-Source-Attestation
4
+ status: Experimental
5
+ applies_to: stigmem v0.9.0a1
6
+ last_updated: 2026-05-21
7
+ owned_risks: []
8
+ contributed_risks:
9
+ - R-22
10
+ ---
11
+
12
+ # Source Attestation Security
13
+
14
+ This file is a compatibility pointer for existing
15
+ `experimental/source-attestation/security.md` links.
16
+
17
+ The canonical ADR-020 feature security record now lives at
18
+ [`features/source-attestation/security.md`](../../features/source-attestation/security.md).
19
+
20
+ This compatibility file remains registered for the security-documentation
21
+ validator while legacy links migrate. Source attestation contributes to R-22
22
+ release supply-chain integrity; the canonical analysis is now in the feature
23
+ record.
@@ -0,0 +1,30 @@
1
+ ---
2
+ spec_id: Spec-X6-Source-Attestation
3
+ version: 0.1.0-alpha.0
4
+ status: Experimental
5
+ applies_to: future experimental plugin line
6
+ last_updated: 2026-05-21
7
+ supersedes: pre-reset section 18 source-attestation material
8
+ depends_on:
9
+ - Spec-01-Fact-Model >= 0.1.0-alpha.0
10
+ - Spec-09-Audit-Log >= 0.1.0-alpha.0
11
+ title: Spec-X6-Source-Attestation
12
+ sidebar_label: Source Attestation
13
+ audience: Spec
14
+ description: "Compatibility pointer for source-attestation semantics."
15
+ stability: experimental
16
+ since: 0.9.0a1
17
+ ---
18
+
19
+ # Spec-X6-Source-Attestation
20
+
21
+ This file is a compatibility pointer for existing
22
+ `experimental/source-attestation/` links.
23
+
24
+ The canonical ADR-020 feature record now lives at
25
+ [`features/source-attestation/`](../../features/source-attestation/). The
26
+ canonical normative spec is
27
+ [`features/source-attestation/spec.md`](../../features/source-attestation/spec.md).
28
+
29
+ The implementation package remains in `experimental/source-attestation/` during
30
+ the transition.
@@ -0,0 +1,12 @@
1
+ """Experimental source-attestation plugin scaffold."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .config import SourceAttestationConfig
6
+ from .manifest import PLUGIN_NAME, plugin_manifest
7
+
8
+ __all__ = [
9
+ "PLUGIN_NAME",
10
+ "SourceAttestationConfig",
11
+ "plugin_manifest",
12
+ ]
@@ -0,0 +1,48 @@
1
+ """Configuration schema for the source-attestation plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Mapping
7
+
8
+ from pydantic import BaseModel, model_validator
9
+
10
+
11
+ class SourceAttestationConfig(BaseModel):
12
+ """Operator-controlled gates for experimental source attestation."""
13
+
14
+ enabled: bool = False
15
+ enforce_assert_validation: bool = False
16
+ apply_recall_rank: bool = False
17
+ enforce_federation_inbound: bool = False
18
+ warn_only: bool = True
19
+
20
+ @model_validator(mode="after")
21
+ def _validate_warn_only_boundary(self) -> SourceAttestationConfig:
22
+ enforcing = self.enforce_assert_validation or self.enforce_federation_inbound
23
+ if enforcing and self.warn_only:
24
+ raise ValueError("warn_only must be false when enforcement gates are enabled")
25
+ return self
26
+
27
+
28
+ def load_config_from_env(
29
+ environ: Mapping[str, str] | None = None,
30
+ ) -> SourceAttestationConfig:
31
+ """Load source-attestation plugin gates from environment variables."""
32
+
33
+ env = environ if environ is not None else os.environ
34
+ prefix = "STIGMEM_SOURCE_ATTESTATION_"
35
+ return SourceAttestationConfig(
36
+ enabled=_env_bool(env, f"{prefix}ENABLED"),
37
+ enforce_assert_validation=_env_bool(env, f"{prefix}ENFORCE_ASSERT_VALIDATION"),
38
+ apply_recall_rank=_env_bool(env, f"{prefix}APPLY_RECALL_RANK"),
39
+ enforce_federation_inbound=_env_bool(env, f"{prefix}ENFORCE_FEDERATION_INBOUND"),
40
+ warn_only=_env_bool(env, f"{prefix}WARN_ONLY", default=True),
41
+ )
42
+
43
+
44
+ def _env_bool(env: Mapping[str, str], name: str, *, default: bool = False) -> bool:
45
+ raw = env.get(name)
46
+ if raw is None:
47
+ return default
48
+ return raw.strip().lower() in {"1", "true", "yes", "on"}
@@ -0,0 +1,147 @@
1
+ """Hook handlers for the source-attestation plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from pydantic import ValidationError
8
+ from stigmem_node.entity_normalizer import NormalizationError, normalize_entity_uri
9
+ from stigmem_node.plugins import Allow, Deny, PluginContext, PluginHealth, PluginHealthStatus
10
+
11
+ from .config import load_config_from_env
12
+
13
+
14
+ def config_validate(_ctx: PluginContext, **_: Any) -> Allow | Deny:
15
+ """Validate operator gates before the plugin is registered."""
16
+
17
+ try:
18
+ load_config_from_env()
19
+ except ValidationError as exc:
20
+ return Deny(f"invalid source-attestation plugin config: {exc}")
21
+ return Allow()
22
+
23
+
24
+ def pre_assert_validate(_ctx: PluginContext, **kwargs: Any) -> Allow | Deny:
25
+ """Gate source/identity binding behind explicit plugin configuration.
26
+
27
+ Returns Allow() when the plugin is disabled or assertion enforcement is not
28
+ enabled. When both gates are enabled, the handler rejects declared source
29
+ values that do not match the authenticated principal or its delegated
30
+ source-entity list.
31
+ """
32
+
33
+ config = load_config_from_env()
34
+ if not (config.enabled and config.enforce_assert_validation):
35
+ return Allow()
36
+
37
+ req = kwargs.get("req")
38
+ identity = kwargs.get("identity")
39
+ source = _normalized_or_none(getattr(req, "source", None))
40
+ authorized_sources = _authorized_source_entities(identity)
41
+ if source is not None and source in authorized_sources:
42
+ return Allow()
43
+
44
+ return Deny(
45
+ "source_attestation_failed: declared source does not match authenticated principal"
46
+ )
47
+
48
+
49
+ def recall_rank(_ctx: PluginContext, scored_results: list[Any], **kwargs: Any) -> dict[str, float]:
50
+ """Contribute source-trust score deltas only when explicitly enabled.
51
+
52
+ Returns an empty delta map when the plugin is disabled, recall ranking is
53
+ not enabled, or the active recall weights set source_trust to zero. Only
54
+ enabled deployments add source-trust contribution to caller-owned scoring.
55
+ """
56
+
57
+ config = load_config_from_env()
58
+ if not (config.enabled and config.apply_recall_rank):
59
+ return {}
60
+
61
+ from stigmem_node.source_trust import compute_source_trust
62
+
63
+ identity = kwargs.get("identity")
64
+ weights = kwargs.get("weights")
65
+ source_weight = float(getattr(weights, "source_trust", 0.0))
66
+ if source_weight <= 0.0:
67
+ return {}
68
+
69
+ base_weight = (
70
+ float(getattr(weights, "lexical", 0.0))
71
+ + float(getattr(weights, "semantic", 0.0))
72
+ + float(getattr(weights, "graph", 0.0))
73
+ + source_weight
74
+ + float(getattr(weights, "recency", 0.0))
75
+ )
76
+ if base_weight <= 0.0:
77
+ base_weight = 1.0
78
+
79
+ deltas: dict[str, float] = {}
80
+ for scored in scored_results:
81
+ fact = getattr(scored, "fact", None)
82
+ fact_id = getattr(fact, "id", None)
83
+ source = getattr(fact, "source", None)
84
+ scope = getattr(fact, "scope", None)
85
+ confidence = float(getattr(fact, "confidence", 0.0))
86
+ if fact_id is None or source is None or scope is None:
87
+ continue
88
+ trust = compute_source_trust(source, scope, identity)
89
+ deltas[str(fact_id)] = (source_weight * trust / base_weight) * max(0.0, confidence)
90
+
91
+ return deltas
92
+
93
+
94
+ def federation_inbound_validate(_ctx: PluginContext, **kwargs: Any) -> Allow | Deny:
95
+ """Gate inbound source-attestation policy behind explicit plugin configuration.
96
+
97
+ Returns Allow() when the plugin is disabled or federation-inbound
98
+ enforcement is not enabled. When both gates are enabled, the handler
99
+ rejects federated facts whose normalized source does not match the peer
100
+ node identifier or capability-token subject.
101
+ """
102
+
103
+ config = load_config_from_env()
104
+ if not (config.enabled and config.enforce_federation_inbound):
105
+ return Allow()
106
+
107
+ fact = kwargs.get("fact") or {}
108
+ fact_source = _normalized_or_none(fact.get("source")) if isinstance(fact, dict) else None
109
+ peer = kwargs.get("peer") or {}
110
+ cap_token = kwargs.get("cap_token") or {}
111
+ expected_source = None
112
+ if isinstance(peer, dict):
113
+ expected_source = peer.get("node_id")
114
+ if expected_source is None and isinstance(cap_token, dict):
115
+ expected_source = cap_token.get("subject")
116
+ if fact_source is not None and fact_source == _normalized_or_none(expected_source):
117
+ return Allow()
118
+
119
+ return Deny("source_attestation_failed: federated fact source does not match sender")
120
+
121
+
122
+ def health_check(_ctx: PluginContext) -> PluginHealth:
123
+ """Report scaffold health for registry lifecycle tests."""
124
+
125
+ return PluginHealth(
126
+ status=PluginHealthStatus.HEALTHY,
127
+ message="source attestation plugin scaffold registered",
128
+ )
129
+
130
+
131
+ def _authorized_source_entities(identity: Any) -> set[str]:
132
+ source_entities = {getattr(identity, "entity_uri", None)}
133
+ source_entities.update(getattr(identity, "allowed_source_entities", ()) or ())
134
+ return {
135
+ normalized
136
+ for raw in source_entities
137
+ if (normalized := _normalized_or_none(raw)) is not None
138
+ }
139
+
140
+
141
+ def _normalized_or_none(raw: Any) -> str | None:
142
+ if not isinstance(raw, str):
143
+ return None
144
+ try:
145
+ return normalize_entity_uri(raw)
146
+ except NormalizationError:
147
+ return None
@@ -0,0 +1,43 @@
1
+ """Plugin manifest factory for source attestation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from stigmem_node.plugins import PluginManifest
6
+
7
+ from . import handlers
8
+ from .config import SourceAttestationConfig
9
+
10
+ PLUGIN_NAME = "stigmem-plugin-source-attestation"
11
+ PLUGIN_VERSION = "0.1.0"
12
+ REQUIRES_STIGMEM = ">=0.9.0a3"
13
+
14
+
15
+ def plugin_manifest() -> PluginManifest:
16
+ """Return the entry-point manifest consumed by ``stigmem.plugins`` discovery."""
17
+
18
+ return PluginManifest(
19
+ name=PLUGIN_NAME,
20
+ version=PLUGIN_VERSION,
21
+ requires_stigmem=REQUIRES_STIGMEM,
22
+ capabilities=frozenset(
23
+ {
24
+ "facts.read",
25
+ "facts.write",
26
+ "recall.read",
27
+ "federation.read",
28
+ "federation.write",
29
+ "identity.read",
30
+ "audit.emit",
31
+ "config.read",
32
+ }
33
+ ),
34
+ hooks={
35
+ "config_validate": handlers.config_validate,
36
+ "pre_assert_validate": handlers.pre_assert_validate,
37
+ "recall_rank": handlers.recall_rank,
38
+ "federation_inbound_validate": handlers.federation_inbound_validate,
39
+ },
40
+ config_schema=SourceAttestationConfig,
41
+ health_check=handlers.health_check,
42
+ async_safe=True,
43
+ )