auths-python 0.1.0__cp38-abi3-win_amd64.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.
auths/trust.py ADDED
@@ -0,0 +1,169 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import json
5
+ from dataclasses import dataclass
6
+ from typing import Optional
7
+
8
+ from auths._native import (
9
+ get_pinned_identity as _get_pinned_identity,
10
+ list_pinned_identities as _list_pinned_identities,
11
+ pin_identity as _pin_identity,
12
+ remove_pinned_identity as _remove_pinned_identity,
13
+ )
14
+ from auths._client import _map_error
15
+ from auths._errors import AuthsError
16
+
17
+
18
+ class TrustLevel(enum.Enum):
19
+ """Trust level for a pinned identity.
20
+
21
+ Values match the Rust ``TrustLevel`` enum in ``auths-core/src/trust/pinned.rs``.
22
+ """
23
+
24
+ TOFU = "tofu"
25
+ """Accepted on first use (interactive prompt)."""
26
+ MANUAL = "manual"
27
+ """Manually pinned via CLI or ``--issuer-pk``."""
28
+ ORG_POLICY = "org_policy"
29
+ """Loaded from roots.json org policy file."""
30
+
31
+
32
+ @dataclass
33
+ class TrustEntry:
34
+ """A pinned trusted identity."""
35
+
36
+ did: str
37
+ label: Optional[str]
38
+ trust_level: str
39
+ first_seen: str
40
+ kel_sequence: Optional[int]
41
+ pinned_at: str
42
+
43
+ @property
44
+ def trust_level_enum(self) -> TrustLevel:
45
+ """Parse the trust_level string into a typed :class:`TrustLevel` enum."""
46
+ return TrustLevel(self.trust_level)
47
+
48
+
49
+ class TrustService:
50
+ """Resource service for trust anchor management."""
51
+
52
+ def __init__(self, client):
53
+ self._client = client
54
+
55
+ def pin(
56
+ self,
57
+ did: str,
58
+ label: str | None = None,
59
+ trust_level: str = "manual",
60
+ repo_path: str | None = None,
61
+ ) -> TrustEntry:
62
+ """Pin an identity as trusted.
63
+
64
+ Args:
65
+ did: The DID to trust (`did:keri:...` or `did:key:...`).
66
+ label: Optional human-readable label.
67
+ trust_level: One of "tofu", "manual", "org_policy".
68
+
69
+ Usage:
70
+ entry = client.trust.pin(identity.did, label="peer")
71
+ """
72
+ rp = repo_path or self._client.repo_path
73
+ try:
74
+ did_out, lbl, tl, first_seen, kel_seq, pinned_at = _pin_identity(
75
+ did, rp, label, trust_level,
76
+ )
77
+ return TrustEntry(
78
+ did=did_out,
79
+ label=lbl,
80
+ trust_level=tl,
81
+ first_seen=first_seen,
82
+ kel_sequence=kel_seq,
83
+ pinned_at=pinned_at,
84
+ )
85
+ except (ValueError, RuntimeError) as exc:
86
+ raise _map_error(exc, default_cls=AuthsError) from exc
87
+
88
+ def remove(
89
+ self,
90
+ did: str,
91
+ repo_path: str | None = None,
92
+ ) -> None:
93
+ """Remove a pinned identity from the trust store.
94
+
95
+ Usage:
96
+ client.trust.remove(identity.did)
97
+ """
98
+ rp = repo_path or self._client.repo_path
99
+ try:
100
+ _remove_pinned_identity(did, rp)
101
+ except (ValueError, RuntimeError) as exc:
102
+ raise _map_error(exc, default_cls=AuthsError) from exc
103
+
104
+ def list(
105
+ self,
106
+ repo_path: str | None = None,
107
+ ) -> list[TrustEntry]:
108
+ """List all pinned trusted identities.
109
+
110
+ Usage:
111
+ entries = client.trust.list()
112
+ """
113
+ rp = repo_path or self._client.repo_path
114
+ try:
115
+ raw = _list_pinned_identities(rp)
116
+ data = json.loads(raw)
117
+ return [
118
+ TrustEntry(
119
+ did=e["did"],
120
+ label=e.get("label"),
121
+ trust_level=e["trust_level"],
122
+ first_seen=e["first_seen"],
123
+ kel_sequence=e.get("kel_sequence"),
124
+ pinned_at=e["pinned_at"],
125
+ )
126
+ for e in data
127
+ ]
128
+ except (ValueError, RuntimeError) as exc:
129
+ raise _map_error(exc, default_cls=AuthsError) from exc
130
+
131
+ def get(
132
+ self,
133
+ did: str,
134
+ repo_path: str | None = None,
135
+ ) -> TrustEntry | None:
136
+ """Look up a specific pinned identity. Returns None if not pinned.
137
+
138
+ Usage:
139
+ entry = client.trust.get(identity.did)
140
+ """
141
+ rp = repo_path or self._client.repo_path
142
+ try:
143
+ result = _get_pinned_identity(did, rp)
144
+ if result is None:
145
+ return None
146
+ did_out, lbl, tl, first_seen, kel_seq, pinned_at = result
147
+ return TrustEntry(
148
+ did=did_out,
149
+ label=lbl,
150
+ trust_level=tl,
151
+ first_seen=first_seen,
152
+ kel_sequence=kel_seq,
153
+ pinned_at=pinned_at,
154
+ )
155
+ except (ValueError, RuntimeError) as exc:
156
+ raise _map_error(exc, default_cls=AuthsError) from exc
157
+
158
+ def is_trusted(
159
+ self,
160
+ did: str,
161
+ repo_path: str | None = None,
162
+ ) -> bool:
163
+ """Check whether a DID is in the trust store.
164
+
165
+ Usage:
166
+ if client.trust.is_trusted(identity.did):
167
+ print("Trusted!")
168
+ """
169
+ return self.get(did, repo_path=repo_path) is not None
auths/verify.py ADDED
@@ -0,0 +1,111 @@
1
+ """Witness-based attestation chain verification."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+
8
+ from auths._native import (
9
+ verify_at_time,
10
+ verify_at_time_with_capability,
11
+ verify_attestation,
12
+ verify_attestation_with_capability,
13
+ verify_chain,
14
+ verify_chain_with_capability,
15
+ verify_chain_with_witnesses as _verify_chain_with_witnesses,
16
+ verify_device_authorization,
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class WitnessKey:
22
+ """A witness node's identity and public key."""
23
+
24
+ did: str
25
+ """The witness node's DID."""
26
+ public_key_hex: str
27
+ """Hex-encoded Ed25519 public key of the witness."""
28
+
29
+ def __repr__(self) -> str:
30
+ return f"WitnessKey(did='{self.did[:20]}...')"
31
+
32
+
33
+ @dataclass
34
+ class WitnessConfig:
35
+ """Configuration for witness quorum verification.
36
+
37
+ Examples:
38
+ ```python
39
+ config = WitnessConfig(
40
+ receipts=[receipt1_json, receipt2_json],
41
+ keys=[WitnessKey("did:key:z...", "ab12...")],
42
+ threshold=2,
43
+ )
44
+ ```
45
+ """
46
+
47
+ receipts: list[str]
48
+ """JSON-serialized witness receipt strings."""
49
+ keys: list[WitnessKey]
50
+ """Witness public keys to verify receipts against."""
51
+ threshold: int
52
+ """Minimum number of valid witness receipts required."""
53
+
54
+ def __post_init__(self):
55
+ if self.threshold < 1:
56
+ raise ValueError(f"threshold must be >= 1, got {self.threshold}")
57
+ if self.threshold > len(self.keys):
58
+ raise ValueError(
59
+ f"threshold ({self.threshold}) cannot exceed number of "
60
+ f"witness keys ({len(self.keys)})"
61
+ )
62
+
63
+
64
+ def verify_chain_with_witnesses(
65
+ attestations_json: list[str],
66
+ root_pk_hex: str,
67
+ witnesses: WitnessConfig,
68
+ ):
69
+ """Verify an attestation chain with witness receipt quorum enforcement.
70
+
71
+ Args:
72
+ attestations_json: List of attestation JSON strings, ordered root-to-leaf.
73
+ root_pk_hex: Root identity's Ed25519 public key (hex-encoded).
74
+ witnesses: Witness configuration with receipts, keys, and threshold.
75
+
76
+ Returns:
77
+ VerificationReport with per-link results and witness quorum status.
78
+
79
+ Raises:
80
+ VerificationError: If the chain or witness quorum fails verification.
81
+
82
+ Examples:
83
+ ```python
84
+ report = verify_chain_with_witnesses(chain, root_key, config)
85
+ ```
86
+ """
87
+ keys_json = [
88
+ json.dumps({"did": k.did, "public_key_hex": k.public_key_hex})
89
+ for k in witnesses.keys
90
+ ]
91
+ return _verify_chain_with_witnesses(
92
+ attestations_json,
93
+ root_pk_hex,
94
+ witnesses.receipts,
95
+ keys_json,
96
+ witnesses.threshold,
97
+ )
98
+
99
+
100
+ __all__ = [
101
+ "WitnessConfig",
102
+ "WitnessKey",
103
+ "verify_at_time",
104
+ "verify_at_time_with_capability",
105
+ "verify_attestation",
106
+ "verify_attestation_with_capability",
107
+ "verify_chain",
108
+ "verify_chain_with_capability",
109
+ "verify_chain_with_witnesses",
110
+ "verify_device_authorization",
111
+ ]
auths/witness.py ADDED
@@ -0,0 +1,91 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Optional
6
+
7
+ from auths._native import (
8
+ add_witness as _add_witness,
9
+ list_witnesses as _list_witnesses,
10
+ remove_witness as _remove_witness,
11
+ )
12
+ from auths._client import _map_error
13
+ from auths._errors import AuthsError
14
+
15
+
16
+ @dataclass
17
+ class Witness:
18
+ """A configured witness endpoint."""
19
+
20
+ url: str
21
+ did: Optional[str]
22
+ label: Optional[str]
23
+
24
+
25
+ class WitnessService:
26
+ """Resource service for witness configuration."""
27
+
28
+ def __init__(self, client):
29
+ self._client = client
30
+
31
+ def add(
32
+ self,
33
+ url: str,
34
+ label: str | None = None,
35
+ repo_path: str | None = None,
36
+ ) -> Witness:
37
+ """Add a witness URL to the identity configuration.
38
+
39
+ Args:
40
+ url: Witness server URL (e.g. "http://127.0.0.1:3333").
41
+ label: Optional human-readable label.
42
+
43
+ Usage:
44
+ w = client.witnesses.add("https://witness.example.com")
45
+ """
46
+ rp = repo_path or self._client.repo_path
47
+ try:
48
+ url_out, did, lbl = _add_witness(url, rp, label)
49
+ return Witness(url=url_out, did=did, label=lbl)
50
+ except (ValueError, RuntimeError) as exc:
51
+ raise _map_error(exc, default_cls=AuthsError) from exc
52
+
53
+ def remove(
54
+ self,
55
+ url: str,
56
+ repo_path: str | None = None,
57
+ ) -> None:
58
+ """Remove a witness URL from configuration.
59
+
60
+ Usage:
61
+ client.witnesses.remove("https://witness.example.com")
62
+ """
63
+ rp = repo_path or self._client.repo_path
64
+ try:
65
+ _remove_witness(url, rp)
66
+ except (ValueError, RuntimeError) as exc:
67
+ raise _map_error(exc, default_cls=AuthsError) from exc
68
+
69
+ def list(
70
+ self,
71
+ repo_path: str | None = None,
72
+ ) -> list[Witness]:
73
+ """List all configured witnesses.
74
+
75
+ Usage:
76
+ witnesses = client.witnesses.list()
77
+ """
78
+ rp = repo_path or self._client.repo_path
79
+ try:
80
+ raw = _list_witnesses(rp)
81
+ data = json.loads(raw)
82
+ return [
83
+ Witness(
84
+ url=w["url"],
85
+ did=w.get("did"),
86
+ label=w.get("label"),
87
+ )
88
+ for w in data
89
+ ]
90
+ except (ValueError, RuntimeError) as exc:
91
+ raise _map_error(exc, default_cls=AuthsError) from exc
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: auths-python
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: Programming Language :: Rust
8
+ Classifier: Operating System :: MacOS
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Typing :: Typed
13
+ Classifier: Topic :: Security :: Cryptography
14
+ Classifier: Topic :: Software Development :: Version Control :: Git
15
+ Requires-Dist: pyjwt>=2.0 ; extra == 'jwt'
16
+ Requires-Dist: cryptography>=3.0 ; extra == 'jwt'
17
+ Provides-Extra: jwt
18
+ Summary: Auths Python SDK - decentralized identity for developers and AI agents
19
+ Keywords: identity,cryptography,did,signing,verification,git,keri
20
+ License: Apache-2.0
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
23
+ Project-URL: Bug Tracker, https://github.com/auths-dev/auths/issues
24
+ Project-URL: Documentation, https://docs.auths.dev
25
+ Project-URL: Homepage, https://auths.dev
26
+ Project-URL: Repository, https://github.com/auths-dev/auths
27
+
28
+ # Auths Python SDK
29
+
30
+ Decentralized identity for developers and AI agents. Sign, verify, and manage cryptographic identities with Git-native storage.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install auths-python
36
+ ```
37
+
38
+ ## Quick start
39
+
40
+ ```python
41
+ from auths import Auths
42
+
43
+ auths = Auths()
44
+
45
+ # Verify an attestation
46
+ result = auths.verify(attestation_json=data, issuer_key=public_key_hex)
47
+ print(result.valid) # True
48
+
49
+ # Sign bytes
50
+ signature = auths.sign(b"hello world", private_key=secret_key_hex)
51
+ ```
52
+
53
+ ## Identity management
54
+
55
+ ```python
56
+ from auths import Auths
57
+
58
+ auths = Auths(repo_path="~/.auths")
59
+
60
+ # Create a cryptographic identity
61
+ identity = auths.identities.create(label="laptop")
62
+ print(identity.did) # did:keri:EBfd...
63
+
64
+ # Provision an agent (for CI, MCP servers, etc.)
65
+ agent = auths.identities.provision_agent(
66
+ identity.did,
67
+ name="deploy-bot",
68
+ capabilities=["sign"],
69
+ )
70
+
71
+ # Sign using the keychain-stored identity key
72
+ sig = auths.sign_as(b"hello world", identity=identity.did)
73
+
74
+ # Link and manage devices
75
+ device = auths.devices.link(identity_did=identity.did, capabilities=["sign"])
76
+ auths.devices.revoke(device.did, identity_did=identity.did, note="replaced")
77
+ ```
78
+
79
+ ## Git commit verification
80
+
81
+ ```python
82
+ from auths.git import verify_commit_range
83
+
84
+ result = verify_commit_range("HEAD~5..HEAD")
85
+ for commit in result.commits:
86
+ print(f"{commit.commit_sha}: {'valid' if commit.is_valid else commit.error}")
87
+ ```
88
+
89
+ ## Capability-aware verification
90
+
91
+ ```python
92
+ # Verify an attestation grants a specific capability
93
+ result = auths.verify(attestation_json=data, issuer_key=key, required_capability="sign_commit")
94
+
95
+ # Verify an entire chain grants a capability
96
+ report = auths.verify_chain(chain, root_key, required_capability="deploy")
97
+ ```
98
+
99
+ ## Agent auth for MCP / AI frameworks
100
+
101
+ ```python
102
+ from auths.agent import AgentAuth
103
+
104
+ auth = AgentAuth(
105
+ bridge_url="https://bridge.example.com",
106
+ attestation_chain_path=".auths/agent-chain.json",
107
+ )
108
+ token = auth.get_token(capabilities=["read", "write"])
109
+ ```
110
+
111
+ ## Error handling
112
+
113
+ ```python
114
+ from auths import Auths, VerificationError, NetworkError
115
+
116
+ auths = Auths()
117
+ try:
118
+ result = auths.verify(attestation_json=data, issuer_key=key)
119
+ except VerificationError as e:
120
+ print(e.code) # "expired_attestation"
121
+ print(e.message) # "Attestation expired at 2024-01-15T..."
122
+ except NetworkError as e:
123
+ if e.should_retry:
124
+ pass # safe to retry
125
+ ```
126
+
127
+ All errors inherit from `AuthsError` and carry `.code`, `.message`, and `.context`.
128
+
129
+ ## Configuration
130
+
131
+ ```python
132
+ # Auto-discover (uses ~/.auths)
133
+ auths = Auths()
134
+
135
+ # Explicit repo path
136
+ auths = Auths(repo_path="/path/to/identity-repo")
137
+
138
+ # With passphrase (or set AUTHS_PASSPHRASE env var)
139
+ auths = Auths(passphrase="my-secret")
140
+
141
+ # Headless / CI mode
142
+ # Set AUTHS_KEYCHAIN_BACKEND=file for environments without a system keychain
143
+ ```
144
+
145
+ ## API reference
146
+
147
+ Type stubs are bundled (`py.typed` + `__init__.pyi`). Your editor will show full signatures, docstrings, and return types for all methods.
148
+
149
+ ## License
150
+
151
+ Apache-2.0
152
+
@@ -0,0 +1,28 @@
1
+ auths/__init__.py,sha256=xvo9RtGBE676iNFg5I11KdlnXJWhapVEEsVRW3xVgz8,3956
2
+ auths/__init__.pyi,sha256=qXSbL9GNgJcb-f1yqU-CkhiKKdIcRiIQUROQefDpdRs,18303
3
+ auths/_client.py,sha256=vL2f2E-UF6gvIKfhsmzdrk0y6clXSg5bV7SAh0eQh4c,26469
4
+ auths/_errors.py,sha256=deTIgyCcUB_McrhVHk-vHHzXMPMe0ft748tpqgQwgpQ,2223
5
+ auths/_native.pyd,sha256=OcyhwpsLTdKNt9AVJK85sEqWO6AIl8v_4bZ36qAhyqw,13433856
6
+ auths/agent.py,sha256=OWIzWdbSs7CA2hdY21BM8qZFXL-8s7ygMUdVUw8PpLw,1713
7
+ auths/artifact.py,sha256=IbtHM6pJhxTdPkYx6k3veTVS49wQ4VI1yGtWt1cl4FE,2070
8
+ auths/attestation_query.py,sha256=KFuGXUxcpEuXawhFVzxAiqosGRaio-GtVhcuJtx10KQ,4573
9
+ auths/audit.py,sha256=5vQvrJU2vomIPRJeSzuHwN2lGoV696bsMWq5G-e-6Pk,7297
10
+ auths/commit.py,sha256=a2ue91XW2mnE5xkYVB-RSNDSHgSJf8Kzo2x8-IZvvms,870
11
+ auths/devices.py,sha256=QYjOP_fYZd2gYfe1EjPq-r-wWBT_G_zZU9xQ912RNEk,5085
12
+ auths/doctor.py,sha256=NCbY88pJLmdSqDNISL_NBIVSGFPvG7SVj4cS9k0eWZY,3061
13
+ auths/git.py,sha256=NXQL6DLaT0NxkvIbHXMDX2maZW1r2bFXEtCEqmUcmfc,15925
14
+ auths/identity.py,sha256=QGf-GAODUItdUJo0dI4Lp8PXzdL3vTIvuxu-WG50gWY,7767
15
+ auths/jwt.py,sha256=Q0_U578ncvOakYkiuZEnxW3FzPZ99IZHQB7010ubA5Y,8878
16
+ auths/org.py,sha256=Ok_uWFZQp8EDwnffh8GS2Tg98YHWw0qCM_jbgufKp-o,10330
17
+ auths/pairing.py,sha256=QXsFWvq-ZE1V65TbFZTbSbaP67yZ8f2Zc6eDfyW_Sko,6858
18
+ auths/policy.py,sha256=23kXn3tmq6x2jweK5XMhAAVSZrIuICq5KVKoglhL3dw,12616
19
+ auths/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ auths/rotation.py,sha256=fX64VxZKW7YMge8LU3OJ1LmDs5iH4acCAU1FYBH_XQk,900
21
+ auths/sign.py,sha256=sGIiKw8MYfnP3oWbepVAUQmhExWkC0D4ZvVg7XAFQrs,200
22
+ auths/trust.py,sha256=YbvxBkPxbu7kycJyQI_d5foyINPnP8H-AklkqqLbVuw,5108
23
+ auths/verify.py,sha256=ymSJNcsgyknAVCA5mP05aT-jaZD1d7oAEoNHbqac31s,3110
24
+ auths/witness.py,sha256=WvVnpZZFZCb8RygGs2ZBE6Y0pUfktwq6NN56rvT0DhI,2562
25
+ auths_python-0.1.0.dist-info/METADATA,sha256=ZUhJJoUboVMu3nhHyOOV5iq1yXvZ9ItbyX9cbzwHfSg,4304
26
+ auths_python-0.1.0.dist-info/WHEEL,sha256=pR3QVM8fbZ6NHAmOlF_iyjo3X1LezoW7fOuSwC4jKRw,95
27
+ auths_python-0.1.0.dist-info/sboms/auths-python.cyclonedx.json,sha256=4o2K2EYZQEFxp5PXZZN0NOJXYclZ1uhxI5z2lvs2-0E,479513
28
+ auths_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.12.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp38-abi3-win_amd64