aethergovern 2.0.0__py3-none-any.whl

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.
aethergovern/cli.py ADDED
@@ -0,0 +1,142 @@
1
+ """
2
+ AetherGovern — Command-line interface (v2.0.0).
3
+
4
+ Usage:
5
+ python -m aethergovern.cli info
6
+ python -m aethergovern.cli verify
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+
13
+ from aethergovern.__version__ import (
14
+ __version__,
15
+ __author__,
16
+ __email__,
17
+ __doi__,
18
+ __license__,
19
+ __paper_version__,
20
+ __record_id__,
21
+ )
22
+
23
+
24
+ def cmd_info(args) -> int:
25
+ """Print version, DOI, and record information."""
26
+ print("=" * 60)
27
+ print(f"AetherGovern v{__version__}")
28
+ print("=" * 60)
29
+ print(f"Author: {__author__}")
30
+ print(f"Email: {__email__}")
31
+ print(f"License: {__license__}")
32
+ print(f"Paper version: {__paper_version__}")
33
+ print(f"Record ID: {__record_id__}")
34
+ print(f"DOI: {__doi__}")
35
+ print(f"DOI URL: https://doi.org/{__doi__}")
36
+ print()
37
+ print("AetherGovern is the fifth functional dimension of the Aether family:")
38
+ print(" AetherGit (storage) — 10.5281/zenodo.23966896")
39
+ print(" AetherFlow (integration) — 10.5281/zenodo.23979129")
40
+ print(" AetherProof (verification) — 10.5281/zenodo.24013124")
41
+ print(" AetherMesh (decentralization) — 10.5281/zenodo.24053492")
42
+ print(" AetherGovern (governance) — 10.5281/zenodo.24054524")
43
+ print()
44
+ print("Status: Architectural Hypothesis Under Investigation (v0.1.0).")
45
+ print(" No real-world deployment. Theorems P1–P2 are proof sketches.")
46
+ print("=" * 60)
47
+ return 0
48
+
49
+
50
+ def cmd_verify(args) -> int:
51
+ """Run a quick self-check."""
52
+ print("=" * 60)
53
+ print(f"AetherGovern v{__version__} — self-check")
54
+ print("=" * 60)
55
+
56
+ ok = True
57
+
58
+ # 1. Ed25519 signature
59
+ print("[1/3] Ed25519 signature verification")
60
+ from aethergovern.authorization.certificates import generate_keypair
61
+ priv, pub = generate_keypair()
62
+ msg = b"test message"
63
+ sig = priv.sign(msg)
64
+ try:
65
+ pub.verify(sig, msg)
66
+ print(" ✅ Valid signature verified")
67
+ except Exception:
68
+ print(" ❌ Signature verification failed")
69
+ ok = False
70
+
71
+ # 2. Frontier join
72
+ print("[2/3] Revocation frontier join")
73
+ from aethergovern.models import RevocationFrontier
74
+ from aethergovern.revocation.frontier import join_frontiers
75
+ f1 = RevocationFrontier("f1", frozenset(["r1", "r2"]), "2026-11-09T00:00:00Z")
76
+ f2 = RevocationFrontier("f2", frozenset(["r2", "r3"]), "2026-11-09T00:00:00Z")
77
+ joined = join_frontiers(f1, f2)
78
+ if joined.revocations == frozenset(["r1", "r2", "r3"]):
79
+ print(f" ✅ Frontier join: {sorted(joined.revocations)}")
80
+ else:
81
+ print(f" ❌ Frontier join failed: {joined.revocations}")
82
+ ok = False
83
+
84
+ # 3. Policy evaluation
85
+ print("[3/3] Policy evaluation")
86
+ from aethergovern.models import (
87
+ Action, ActionKind, Policy, PolicyStatus, PolicyVerdict, Rule
88
+ )
89
+ from aethergovern.policy.enforcement import apply_policy, policy_hash
90
+ policy = Policy(
91
+ policy_id="p1", version=1,
92
+ rules=[Rule("commit", "branch/main", PolicyVerdict.ALLOW)],
93
+ approval_chain=["agent-1"],
94
+ status=PolicyStatus.ENFORCED,
95
+ created_at="2026-11-01T00:00:00Z",
96
+ effective_from="2026-11-01T00:00:00Z",
97
+ )
98
+ action = Action(
99
+ action_id="a"*64, kind=ActionKind.COMMIT, actor="agent-1",
100
+ resource="branch/main", payload={},
101
+ timestamp="2026-11-09T00:00:00Z",
102
+ frontier_ref="f"*64, policy_ref=policy_hash(policy),
103
+ authority_chain=["c"*64], signature=b"\x00"*64,
104
+ )
105
+ verdict = apply_policy(action, policy)
106
+ if verdict == PolicyVerdict.ALLOW:
107
+ print(f" ✅ Policy verdict: {verdict.value}")
108
+ else:
109
+ print(f" ❌ Policy verdict: {verdict.value}")
110
+ ok = False
111
+
112
+ print("=" * 60)
113
+ if ok:
114
+ print(" ✅ All checks passed")
115
+ return 0
116
+ else:
117
+ print(" ❌ Some checks failed")
118
+ return 1
119
+
120
+
121
+ def main(argv=None) -> int:
122
+ """Main entry point."""
123
+ import argparse
124
+ parser = argparse.ArgumentParser(
125
+ prog="aethergovern",
126
+ description=f"AetherGovern v{__version__} — falsification-first governance",
127
+ )
128
+ parser.add_argument("--version", action="version", version=f"aethergovern {__version__}")
129
+ sub = parser.add_subparsers(dest="command", required=True)
130
+
131
+ p_info = sub.add_parser("info", help="Print version and record info")
132
+ p_info.set_defaults(func=cmd_info)
133
+
134
+ p_verify = sub.add_parser("verify", help="Run a self-check")
135
+ p_verify.set_defaults(func=cmd_verify)
136
+
137
+ args = parser.parse_args(argv)
138
+ return args.func(args)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ sys.exit(main())
@@ -0,0 +1,11 @@
1
+ """AetherGovern — Dispute resolution module (Layer 5)."""
2
+
3
+ from aethergovern.dispute.resolution import (
4
+ escalate,
5
+ resolve_by_rule,
6
+ )
7
+
8
+ __all__ = [
9
+ "escalate",
10
+ "resolve_by_rule",
11
+ ]
@@ -0,0 +1,132 @@
1
+ """
2
+ AetherGovern — Dispute resolution (Layer 5, v2.0.0).
3
+
4
+ Implements:
5
+ - escalate: escalate an unresolved dispute to human review
6
+ - resolve_by_rule: resolve a dispute by a deterministic rule
7
+
8
+ Reference:
9
+ Baladi, S. (2026). AetherGovern v2.0.0,
10
+ docs/specification/02_operations.md, Operation 6 (escalate).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from datetime import datetime, timezone
16
+ from typing import Dict
17
+
18
+ from aethergovern.models import (
19
+ Dispute,
20
+ DisputeResolution,
21
+ )
22
+
23
+
24
+ # =============================================================================
25
+ # Time helpers
26
+ # =============================================================================
27
+
28
+ def _now_iso() -> str:
29
+ return datetime.now(timezone.utc).isoformat()
30
+
31
+
32
+ # =============================================================================
33
+ # escalate
34
+ # =============================================================================
35
+
36
+ def escalate(
37
+ dispute: Dispute,
38
+ reason: str,
39
+ escalation_target: str,
40
+ ) -> Dispute:
41
+ """
42
+ Escalate an unresolved dispute to human review.
43
+
44
+ Preconditions:
45
+ - dispute.resolution == PENDING or ESCALATED
46
+ - escalation_target is a non-empty string
47
+
48
+ Postconditions:
49
+ - result.resolution == ESCALATED
50
+ - result.escalation_record contains reason and escalation_target
51
+
52
+ Invariants:
53
+ I9.2 — Escalation completeness: escalation_record must be non-empty.
54
+ I9.3 — No silent resolution: the dispute is explicitly escalated.
55
+ """
56
+ if dispute.resolution == DisputeResolution.RESOLVED_BY_RULE:
57
+ raise ValueError(
58
+ f"dispute {dispute.dispute_id} is already resolved by rule "
59
+ f"(rule={dispute.precedence_rule})"
60
+ )
61
+ if not escalation_target:
62
+ raise ValueError("empty escalation target")
63
+ if not reason:
64
+ raise ValueError("empty escalation reason")
65
+
66
+ escalation_record: Dict = {
67
+ "reason": reason,
68
+ "escalation_target": escalation_target,
69
+ "escalated_at": _now_iso(),
70
+ }
71
+
72
+ return Dispute(
73
+ dispute_id=dispute.dispute_id,
74
+ claim_a=dispute.claim_a,
75
+ claim_b=dispute.claim_b,
76
+ precedence_rule=dispute.precedence_rule,
77
+ resolution=DisputeResolution.ESCALATED,
78
+ escalation_record=escalation_record,
79
+ created_at=dispute.created_at,
80
+ )
81
+
82
+
83
+ # =============================================================================
84
+ # resolve_by_rule
85
+ # =============================================================================
86
+
87
+ def resolve_by_rule(dispute: Dispute, rule: str) -> Dispute:
88
+ """
89
+ Resolve a dispute by a deterministic precedence rule.
90
+
91
+ Supported rules (for v2.0.0):
92
+ - "more_restrictive": the claim with the smaller scope wins.
93
+ - "later_expiry": the claim with the later expiry wins
94
+ (approximated by chain length for v2.0.0).
95
+
96
+ Preconditions:
97
+ - dispute.resolution == PENDING
98
+ - rule is a non-empty string
99
+
100
+ Postconditions:
101
+ - result.resolution == RESOLVED_BY_RULE
102
+ - result.precedence_rule == rule
103
+
104
+ Invariants:
105
+ I9.1 — Determinism: the same input always gives the same resolution.
106
+ I9.3 — No silent resolution: the rule is explicitly recorded.
107
+ """
108
+ if dispute.resolution == DisputeResolution.RESOLVED_BY_RULE:
109
+ raise ValueError(
110
+ f"dispute {dispute.dispute_id} is already resolved "
111
+ f"(rule={dispute.precedence_rule})"
112
+ )
113
+ if dispute.resolution == DisputeResolution.ESCALATED:
114
+ raise ValueError(
115
+ f"dispute {dispute.dispute_id} has been escalated; "
116
+ f"cannot resolve by rule"
117
+ )
118
+ if not rule:
119
+ raise ValueError("empty rule")
120
+
121
+ # Deterministic resolution: the rule determines the winner.
122
+ # For v2.0.0, we only record the rule; the winner is determined by the
123
+ # caller (or a future implementation of the rule).
124
+ return Dispute(
125
+ dispute_id=dispute.dispute_id,
126
+ claim_a=dispute.claim_a,
127
+ claim_b=dispute.claim_b,
128
+ precedence_rule=rule,
129
+ resolution=DisputeResolution.RESOLVED_BY_RULE,
130
+ escalation_record=dispute.escalation_record,
131
+ created_at=dispute.created_at,
132
+ )
aethergovern/models.py ADDED
@@ -0,0 +1,334 @@
1
+ """
2
+ AetherGovern — Core data models (v2.0.0).
3
+
4
+ This module defines the ten entities of AetherGovern v2.0.0:
5
+
6
+ 1. Agent — actor identity
7
+ 2. Action — operation (commit, merge, delete, deploy, rotate_key)
8
+ 3. Resource — target (branch, path, key)
9
+ 4. DelegationCertificate — signed grant of scope
10
+ 5. RevocationRecord — signed withdrawal of a certificate
11
+ 6. Policy — rule that governs authorization
12
+ 7. RevocationFrontier — set of revocation records an actor has seen
13
+ 8. AuthorityChain — sequence of certificates from root to leaf
14
+ 9. Dispute — conflict between two authority claims
15
+ 10. AccountabilityRecord — after-the-fact trace of an admitted action
16
+
17
+ Reference:
18
+ Baladi, S. (2026). AetherGovern: A Falsification-First Investigation
19
+ of Governance in the Aether Family. Zenodo.
20
+ https://doi.org/10.5281/zenodo.24054524
21
+
22
+ v2.0.0 Specification, docs/specification/01_entities.md.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from dataclasses import dataclass, field
28
+ from enum import Enum
29
+ from typing import Dict, FrozenSet, List, Optional, Tuple
30
+
31
+
32
+ # =============================================================================
33
+ # Enums
34
+ # =============================================================================
35
+
36
+ class AgentStatus(str, Enum):
37
+ """Status of an agent."""
38
+ ACTIVE = "active"
39
+ SUSPENDED = "suspended"
40
+ REVOKED = "revoked"
41
+
42
+
43
+ class ActionKind(str, Enum):
44
+ """Kind of action an agent may perform."""
45
+ COMMIT = "commit"
46
+ MERGE = "merge"
47
+ DELETE = "delete"
48
+ DEPLOY = "deploy"
49
+ ROTATE_KEY = "rotate_key"
50
+
51
+
52
+ class ResourceKind(str, Enum):
53
+ """Kind of resource an action targets."""
54
+ BRANCH = "branch"
55
+ PATH = "path"
56
+ KEY = "key"
57
+
58
+
59
+ class PolicyStatus(str, Enum):
60
+ """Status of a policy."""
61
+ PROPOSED = "proposed"
62
+ APPROVED = "approved"
63
+ ENFORCED = "enforced"
64
+ REVOKED = "revoked"
65
+
66
+
67
+ class DisputeResolution(str, Enum):
68
+ """Resolution status of a dispute."""
69
+ PENDING = "pending"
70
+ RESOLVED_BY_RULE = "resolved_by_rule"
71
+ ESCALATED = "escalated"
72
+
73
+
74
+ class PolicyVerdict(str, Enum):
75
+ """Verdict of a policy evaluation."""
76
+ ALLOW = "allow"
77
+ DENY = "deny"
78
+ QUARANTINE = "quarantine"
79
+
80
+
81
+ # =============================================================================
82
+ # Type aliases
83
+ # =============================================================================
84
+
85
+ Scope = FrozenSet[Tuple[str, str]]
86
+ """A scope is a frozen set of (action_kind, resource_id) pairs."""
87
+
88
+
89
+ # =============================================================================
90
+ # Entity 1 — Agent
91
+ # =============================================================================
92
+
93
+ @dataclass(frozen=True)
94
+ class Agent:
95
+ """
96
+ An autonomous software actor.
97
+
98
+ Invariants:
99
+ I1.1 — Key uniqueness (enforced by the registry, not the entity)
100
+ I1.2 — Status monotonicity (enforced by the state machine)
101
+ I1.3 — Registration completeness (enforced by the admission layer)
102
+ """
103
+ agent_id: str
104
+ public_key: bytes
105
+ key_id: str
106
+ role: Optional[str]
107
+ registered_at: str
108
+ status: AgentStatus = AgentStatus.ACTIVE
109
+
110
+
111
+ # =============================================================================
112
+ # Entity 2 — Action
113
+ # =============================================================================
114
+
115
+ @dataclass(frozen=True)
116
+ class Action:
117
+ """
118
+ An operation by an agent on a resource.
119
+
120
+ Invariants:
121
+ I2.1 — Causal citation (enforced by the admission layer)
122
+ I2.2 — Content addressing (enforced by the constructor)
123
+ I2.3 — Signature covers content (enforced by the signature layer)
124
+ I2.4 — Chain completeness (enforced by the admission layer)
125
+ """
126
+ action_id: str
127
+ kind: ActionKind
128
+ actor: str
129
+ resource: str
130
+ payload: Dict
131
+ timestamp: str
132
+ frontier_ref: str
133
+ policy_ref: str
134
+ authority_chain: List[str]
135
+ signature: bytes = b""
136
+
137
+
138
+ # =============================================================================
139
+ # Entity 3 — Resource
140
+ # =============================================================================
141
+
142
+ @dataclass(frozen=True)
143
+ class Resource:
144
+ """
145
+ The target of an action.
146
+
147
+ Invariants:
148
+ I3.1 — Identifier stability (enforced by frozen dataclass)
149
+ I3.2 — Kind immutability (enforced by frozen dataclass)
150
+ """
151
+ resource_id: str
152
+ kind: ResourceKind
153
+ owner: str
154
+ metadata: Dict = field(default_factory=dict)
155
+
156
+
157
+ # =============================================================================
158
+ # Entity 4 — DelegationCertificate
159
+ # =============================================================================
160
+
161
+ @dataclass(frozen=True)
162
+ class DelegationCertificate:
163
+ """
164
+ A signed grant of scope from an issuer to a subject.
165
+
166
+ Invariants:
167
+ I4.1 — Attenuation (enforced by the delegate operation)
168
+ I4.2 — Root recognition (enforced by the check operation)
169
+ I4.3 — Expiry (enforced by the check operation)
170
+ I4.4 — Signature covers content (enforced by the signature layer)
171
+ I4.5 — No cycle (enforced by the parent_ref chain)
172
+ """
173
+ certificate_id: str
174
+ issuer: str
175
+ subject: str
176
+ scope: Scope
177
+ constraints: Dict
178
+ expiry: str
179
+ parent_ref: Optional[str]
180
+ signature: bytes = b""
181
+
182
+
183
+ # =============================================================================
184
+ # Entity 5 — RevocationRecord
185
+ # =============================================================================
186
+
187
+ @dataclass(frozen=True)
188
+ class RevocationRecord:
189
+ """
190
+ A signed withdrawal of a certificate.
191
+
192
+ Invariants:
193
+ I5.1 — Monotonicity (enforced by the frontier)
194
+ I5.2 — Signature covers content (enforced by the signature layer)
195
+ I5.3 — Issuer authority (enforced by the revoke operation)
196
+ """
197
+ revocation_id: str
198
+ certificate_ref: str
199
+ issuer: str
200
+ reason: str
201
+ timestamp: str
202
+ signature: bytes = b""
203
+
204
+
205
+ # =============================================================================
206
+ # Entity 6 — Policy
207
+ # =============================================================================
208
+
209
+ @dataclass(frozen=True)
210
+ class Rule:
211
+ """A single policy rule."""
212
+ action_kind: str
213
+ resource_pattern: str
214
+ verdict: PolicyVerdict
215
+
216
+
217
+ @dataclass(frozen=True)
218
+ class Policy:
219
+ """
220
+ A rule that governs authorization.
221
+
222
+ Invariants:
223
+ I6.1 — Version monotonicity (enforced by the policy registry)
224
+ I6.2 — Approval completeness (enforced by the apply_policy operation)
225
+ I6.3 — Effective date (enforced by the apply_policy operation)
226
+ I6.4 — Supersession chain (enforced by the policy registry)
227
+ """
228
+ policy_id: str
229
+ version: int
230
+ rules: List[Rule]
231
+ approval_chain: List[str]
232
+ status: PolicyStatus
233
+ created_at: str
234
+ effective_from: str
235
+ supersedes: Optional[str] = None
236
+
237
+
238
+ # =============================================================================
239
+ # Entity 7 — RevocationFrontier
240
+ # =============================================================================
241
+
242
+ @dataclass(frozen=True)
243
+ class RevocationFrontier:
244
+ """
245
+ The set of revocation records an actor has seen.
246
+
247
+ Invariants:
248
+ I7.1 — Grow-only (enforced by the frontier operations)
249
+ I7.2 — Join is union (enforced by the join method)
250
+ I7.3 — Content addressing (enforced by the constructor)
251
+ """
252
+ frontier_id: str
253
+ revocations: FrozenSet[str]
254
+ created_at: str
255
+
256
+ def join(self, other: "RevocationFrontier") -> "RevocationFrontier":
257
+ """Join two frontiers: set union of revocations."""
258
+ return RevocationFrontier(
259
+ frontier_id=f"{self.frontier_id}+{other.frontier_id}",
260
+ revocations=self.revocations | other.revocations,
261
+ created_at=self.created_at,
262
+ )
263
+
264
+ def __le__(self, other: "RevocationFrontier") -> bool:
265
+ """Partial order: self ≤ other iff self.revocations ⊆ other.revocations."""
266
+ return self.revocations <= other.revocations
267
+
268
+
269
+ # =============================================================================
270
+ # Entity 8 — AuthorityChain
271
+ # =============================================================================
272
+
273
+ @dataclass(frozen=True)
274
+ class AuthorityChain:
275
+ """
276
+ A sequence of delegation certificates from a root to a leaf agent.
277
+
278
+ Invariants:
279
+ I8.1 — Attenuation preserved (enforced by the check operation)
280
+ I8.2 — Root recognized (enforced by the check operation)
281
+ I8.3 — No cycle (enforced by the certificate chain)
282
+ I8.4 — Contiguity (enforced by the check operation)
283
+ """
284
+ chain_id: str
285
+ certificates: List[str]
286
+ leaf_agent: str
287
+ leaf_scope: Scope
288
+
289
+
290
+ # =============================================================================
291
+ # Entity 9 — Dispute
292
+ # =============================================================================
293
+
294
+ @dataclass(frozen=True)
295
+ class Dispute:
296
+ """
297
+ A conflict between two authority claims.
298
+
299
+ Invariants:
300
+ I9.1 — Determinism (enforced by the escalate operation)
301
+ I9.2 — Escalation completeness (enforced by the escalate operation)
302
+ I9.3 — No silent resolution (enforced by the escalate operation)
303
+ """
304
+ dispute_id: str
305
+ claim_a: AuthorityChain
306
+ claim_b: AuthorityChain
307
+ precedence_rule: Optional[str]
308
+ resolution: DisputeResolution
309
+ escalation_record: Optional[Dict] = None
310
+ created_at: str = ""
311
+
312
+
313
+ # =============================================================================
314
+ # Entity 10 — AccountabilityRecord
315
+ # =============================================================================
316
+
317
+ @dataclass(frozen=True)
318
+ class AccountabilityRecord:
319
+ """
320
+ The after-the-fact trace of an admitted action.
321
+
322
+ Invariants:
323
+ I10.1 — Immutability (enforced by frozen dataclass)
324
+ I10.2 — Hash-link integrity (enforced by the record chain)
325
+ I10.3 — Attribution completeness (enforced by the admission layer)
326
+ I10.4 — Signature covers content (enforced by the signature layer)
327
+ """
328
+ record_id: str
329
+ action_ref: str
330
+ authority_chain_ref: str
331
+ admitted_at: str
332
+ admitted_by: str
333
+ prev_record_hash: str
334
+ signature: bytes = b""
@@ -0,0 +1,11 @@
1
+ """AetherGovern — Policy module (Layer 2)."""
2
+
3
+ from aethergovern.policy.enforcement import (
4
+ policy_hash,
5
+ apply_policy,
6
+ )
7
+
8
+ __all__ = [
9
+ "policy_hash",
10
+ "apply_policy",
11
+ ]