ltcai 1.0.1 → 1.2.0

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,152 @@
1
+ """Enterprise extension seam (open-core boundary).
2
+
3
+ LatticeAI Community is fully functional on its own. This module defines the
4
+ *seam* where a future, separately-distributed Enterprise plugin can attach
5
+ advanced capabilities (SSO provisioning, SCIM, tenant isolation, compliance
6
+ retention, SIEM export, and so on) **without** any of that code living in the
7
+ public Community repository.
8
+
9
+ Design rules enforced here:
10
+
11
+ * Community is the default :class:`Edition`. Every :class:`EnterpriseCapability`
12
+ is **disabled** unless an Enterprise provider is explicitly registered.
13
+ * Nothing in this module restricts or gates a Community feature. It only answers
14
+ "is this *Enterprise* capability available?" — which is ``False`` in the open
15
+ build.
16
+ * Enterprise behaviour is supplied at runtime through
17
+ :class:`CapabilityProvider` implementations registered on the shared
18
+ :data:`capability_registry`. The public code never imports Enterprise code.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import os
24
+ from enum import Enum
25
+ from typing import Dict, List, Optional, Protocol, runtime_checkable
26
+
27
+
28
+ class Edition(str, Enum):
29
+ """Distribution edition. Community is the open-source default."""
30
+
31
+ COMMUNITY = "community"
32
+ ENTERPRISE = "enterprise"
33
+
34
+
35
+ class EnterpriseCapability(str, Enum):
36
+ """Capabilities reserved for the Enterprise edition.
37
+
38
+ These are documented in ``docs/ENTERPRISE.md``. They are intentionally
39
+ declared (so the seam is stable and discoverable) but never implemented in
40
+ the Community build.
41
+ """
42
+
43
+ SSO_ADVANCED = "sso_advanced"
44
+ IDP_PROVISIONING = "idp_provisioning" # Entra ID / Okta provisioning
45
+ SCIM = "scim"
46
+ RBAC_ABAC_ADVANCED = "rbac_abac_advanced"
47
+ TENANT_ISOLATION = "tenant_isolation"
48
+ COMPLIANCE_RETENTION = "compliance_retention"
49
+ SIEM_EXPORT = "siem_export"
50
+ PRIVATE_VPC = "private_vpc"
51
+ AIR_GAPPED_DEPLOYMENT = "air_gapped_deployment"
52
+ DLP_POLICY = "dlp_policy"
53
+ EDISCOVERY = "ediscovery"
54
+ ADMIN_POLICY_PACKS = "admin_policy_packs"
55
+
56
+
57
+ @runtime_checkable
58
+ class CapabilityProvider(Protocol):
59
+ """Contract an Enterprise plugin implements to light up capabilities.
60
+
61
+ A provider is registered at runtime via
62
+ :meth:`CapabilityRegistry.register_provider`. The Community build ships no
63
+ provider, so :meth:`is_enabled` is effectively ``False`` everywhere.
64
+ """
65
+
66
+ def edition(self) -> Edition:
67
+ ...
68
+
69
+ def is_enabled(self, capability: EnterpriseCapability) -> bool:
70
+ ...
71
+
72
+
73
+ class CommunityCapabilityProvider:
74
+ """Default provider: Community edition, no Enterprise capabilities."""
75
+
76
+ def edition(self) -> Edition:
77
+ return Edition.COMMUNITY
78
+
79
+ def is_enabled(self, capability: EnterpriseCapability) -> bool: # noqa: ARG002
80
+ return False
81
+
82
+
83
+ class CapabilityRegistry:
84
+ """Runtime seam that an Enterprise plugin can attach a provider to.
85
+
86
+ The registry holds at most one active provider. Until an Enterprise build
87
+ registers one, the :class:`CommunityCapabilityProvider` answers every query
88
+ with "Community / disabled".
89
+ """
90
+
91
+ def __init__(self) -> None:
92
+ self._provider: CapabilityProvider = CommunityCapabilityProvider()
93
+
94
+ def register_provider(self, provider: CapabilityProvider) -> None:
95
+ if not isinstance(provider, CapabilityProvider):
96
+ raise TypeError("provider must implement the CapabilityProvider protocol")
97
+ self._provider = provider
98
+
99
+ def reset(self) -> None:
100
+ """Restore the Community default provider (used by tests)."""
101
+ self._provider = CommunityCapabilityProvider()
102
+
103
+ def edition(self) -> Edition:
104
+ return self._provider.edition()
105
+
106
+ def is_capability_enabled(self, capability: EnterpriseCapability) -> bool:
107
+ try:
108
+ return bool(self._provider.is_enabled(capability))
109
+ except Exception:
110
+ # A misbehaving plugin must never break a Community request.
111
+ return False
112
+
113
+ def available_capabilities(self) -> List[EnterpriseCapability]:
114
+ return [cap for cap in EnterpriseCapability if self.is_capability_enabled(cap)]
115
+
116
+ def describe(self) -> Dict[str, object]:
117
+ """Edition + capability matrix for ``/workspace/editions`` and admin UI."""
118
+ return {
119
+ "edition": self.edition().value,
120
+ "is_enterprise": self.edition() is Edition.ENTERPRISE,
121
+ "capabilities": {
122
+ cap.value: self.is_capability_enabled(cap) for cap in EnterpriseCapability
123
+ },
124
+ "community_notice": (
125
+ "All listed capabilities are Enterprise-only extension points. "
126
+ "The open-source Community edition ships none of them enabled; "
127
+ "see docs/ENTERPRISE.md and docs/EDITION_STRATEGY.md."
128
+ ),
129
+ }
130
+
131
+
132
+ # Shared singleton seam. Enterprise plugins import this and call
133
+ # ``capability_registry.register_provider(...)`` during their own startup.
134
+ capability_registry = CapabilityRegistry()
135
+
136
+
137
+ def detect_edition() -> Edition:
138
+ """Best-effort edition detection.
139
+
140
+ Honours an explicit ``LATTICE_EDITION=enterprise`` opt-in for environments
141
+ that have separately installed an Enterprise provider, but otherwise the
142
+ registry's active provider is the source of truth.
143
+ """
144
+ env = os.environ.get("LATTICE_EDITION", "").strip().lower()
145
+ if env == Edition.ENTERPRISE.value and capability_registry.edition() is Edition.ENTERPRISE:
146
+ return Edition.ENTERPRISE
147
+ return capability_registry.edition()
148
+
149
+
150
+ def is_capability_enabled(capability: EnterpriseCapability) -> bool:
151
+ """Module-level convenience used by Community code at extension points."""
152
+ return capability_registry.is_capability_enabled(capability)