capauth 0.1.2__tar.gz → 0.2.3__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.
- capauth-0.2.3/PKG-INFO +256 -0
- capauth-0.2.3/README.md +201 -0
- {capauth-0.1.2 → capauth-0.2.3}/pyproject.toml +8 -3
- capauth-0.2.3/src/capauth/__init__.py +48 -0
- capauth-0.2.3/src/capauth/agent_identity.py +301 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/api.py +1 -2
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/claims_mapper.py +10 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/stage.py +52 -26
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/verifier.py +2 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/cli.py +228 -24
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/crypto/pgpy_backend.py +1 -3
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/did.py +40 -22
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/discovery/file_discovery.py +2 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/discovery/mdns.py +5 -3
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/discovery/syncthing.py +4 -9
- capauth-0.2.3/src/capauth/integration.py +192 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/config.py +1 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/forgejo_api.py +1 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/oidc_provider.py +19 -9
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/test_auth_flow.py +25 -10
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/test_cli.py +6 -2
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/test_config.py +3 -3
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/test_forgejo_api.py +3 -3
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/test_oidc_provider.py +19 -4
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/login.py +9 -4
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/mesh.py +3 -4
- capauth-0.2.3/src/capauth/migrations/0001_initial.py +102 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/models.py +1 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/pma.py +2 -3
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/profile.py +7 -8
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/registry.py +6 -4
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/service/app.py +98 -28
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/service/keystore.py +6 -2
- capauth-0.2.3/src/capauth/sync.py +272 -0
- capauth-0.2.3/src/capauth.egg-info/PKG-INFO +256 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth.egg-info/SOURCES.txt +6 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth.egg-info/requires.txt +6 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/conftest.py +1 -3
- capauth-0.2.3/tests/test_agent_identity.py +251 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_claims_mapper.py +6 -2
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_crypto.py +1 -3
- capauth-0.2.3/tests/test_home_resolution.py +26 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_identity.py +3 -9
- capauth-0.2.3/tests/test_integration.py +185 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_login.py +63 -31
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_mesh.py +1 -3
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_nextcloud_app.py +60 -34
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_real_pgp_e2e.py +136 -73
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_register_cli.py +38 -19
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_service.py +81 -33
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_syncthing_discovery.py +5 -17
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_verifier.py +17 -5
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_zk_profile.py +8 -8
- capauth-0.1.2/PKG-INFO +0 -722
- capauth-0.1.2/README.md +0 -670
- capauth-0.1.2/src/capauth/__init__.py +0 -7
- capauth-0.1.2/src/capauth/migrations/0001_initial.py +0 -48
- capauth-0.1.2/src/capauth.egg-info/PKG-INFO +0 -722
- {capauth-0.1.2 → capauth-0.2.3}/LICENSE +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/MANIFEST.in +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/setup.cfg +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/apps.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/nonce_store.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/authentik/urls.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/crypto/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/crypto/base.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/crypto/gnupg_backend.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/discovery/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/discovery/base.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/exceptions.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/identity.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/__init__.py +1 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/auth_flow.py +1 -1
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/cli.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/integrations/forgejo/tests/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/migrations/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/service/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth/service/server.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth.egg-info/dependency_links.txt +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth.egg-info/entry_points.txt +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/src/capauth.egg-info/top_level.txt +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/__init__.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_nonce_store.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_pma.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_profile.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_qr_login.py +0 -0
- {capauth-0.1.2 → capauth-0.2.3}/tests/test_registry.py +0 -0
capauth-0.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: capauth
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Capability-based Authentication — decentralized, PGP-based identity and authorization
|
|
5
|
+
Author-email: smilinTux <admin@smilintux.org>
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://capauth.io
|
|
8
|
+
Project-URL: Documentation, https://capauth.io
|
|
9
|
+
Project-URL: Repository, https://github.com/smilinTux/capauth
|
|
10
|
+
Project-URL: Issues, https://github.com/smilinTux/capauth/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/smilinTux/capauth/releases
|
|
12
|
+
Keywords: authentication,pgp,decentralized,identity,capability
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: click>=8.1
|
|
26
|
+
Requires-Dist: pydantic>=2.0
|
|
27
|
+
Requires-Dist: PGPy>=0.6
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Requires-Dist: rich>=13.0
|
|
30
|
+
Requires-Dist: httpx>=0.25
|
|
31
|
+
Provides-Extra: skcapstone
|
|
32
|
+
Requires-Dist: skcapstone>=0.6.8; extra == "skcapstone"
|
|
33
|
+
Provides-Extra: gnupg
|
|
34
|
+
Requires-Dist: python-gnupg>=0.5; extra == "gnupg"
|
|
35
|
+
Provides-Extra: service
|
|
36
|
+
Requires-Dist: fastapi>=0.110; extra == "service"
|
|
37
|
+
Requires-Dist: uvicorn[standard]>=0.27; extra == "service"
|
|
38
|
+
Requires-Dist: PyJWT>=2.8; extra == "service"
|
|
39
|
+
Requires-Dist: python-gnupg>=0.5; extra == "service"
|
|
40
|
+
Requires-Dist: httpx>=0.25; extra == "service"
|
|
41
|
+
Requires-Dist: segno>=1.6; extra == "service"
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
46
|
+
Requires-Dist: httpx>=0.25; extra == "dev"
|
|
47
|
+
Requires-Dist: fastapi>=0.110; extra == "dev"
|
|
48
|
+
Requires-Dist: python-gnupg>=0.5; extra == "dev"
|
|
49
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
50
|
+
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
51
|
+
Requires-Dist: zeroconf>=0.38; extra == "dev"
|
|
52
|
+
Provides-Extra: all
|
|
53
|
+
Requires-Dist: capauth[dev,gnupg,service]; extra == "all"
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
# capauth — Sovereign PGP Identity 🔐
|
|
57
|
+
|
|
58
|
+
> **OAuth is dead. Long live sovereignty.**
|
|
59
|
+
> Your identity is a PGP keypair *you* generated, on hardware *you* own. No
|
|
60
|
+
> "Login with Google", no authorization server in the middle, no revocation risk
|
|
61
|
+
> you don't control. You don't *use* an identity provider — you **are** the
|
|
62
|
+
> identity provider.
|
|
63
|
+
|
|
64
|
+
capauth is the **Core identity capability** of the [SKWorld](https://skworld.io)
|
|
65
|
+
sovereign agent ecosystem. It gives every entity — human *or* AI — one
|
|
66
|
+
cryptographic root: a PGP keypair, a self-hosted **sovereign profile**, and a
|
|
67
|
+
challenge-response proof of who they are that **anyone can verify offline**, with
|
|
68
|
+
no callback to a corporate server. Every other SK layer (skchat, skcomms,
|
|
69
|
+
skmemory, skcapstone) trusts you because capauth proves who you are.
|
|
70
|
+
|
|
71
|
+
**Never used PGP-based auth?** The mental model is simple: instead of an opaque
|
|
72
|
+
bearer token issued by a third party, you sign a random challenge with a key only
|
|
73
|
+
you hold. The verifier checks the signature against your public key. Valid
|
|
74
|
+
signature = authenticated. Done. No middleman ever sees the secret.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## The 60-second version
|
|
79
|
+
|
|
80
|
+
```mermaid
|
|
81
|
+
flowchart LR
|
|
82
|
+
INIT["capauth init<br/>(generate PGP keypair)"] --> PROFILE["sovereign profile<br/>(~/.capauth/, yours alone)"]
|
|
83
|
+
PROFILE --> DID["DID documents<br/>(key / mesh / public)"]
|
|
84
|
+
PROFILE --> VERIFY["challenge-response<br/>(prove identity, offline)"]
|
|
85
|
+
VERIFY --> LOGIN["capauth login <service><br/>(passwordless PGP auth)"]
|
|
86
|
+
LOGIN --> SVC["any OIDC app<br/>(Forgejo · Nextcloud · Immich)"]
|
|
87
|
+
PROFILE --> MESH["peer mesh<br/>(discover & verify peers)"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
You generate a keypair once. From then on, signing a random challenge with your
|
|
91
|
+
private key *is* your login — to a service, to a peer, to the mesh. The key never
|
|
92
|
+
leaves your machine, and the proof is verifiable by anyone holding your public
|
|
93
|
+
key, with zero phone-home.
|
|
94
|
+
|
|
95
|
+
## Where it lives in SKStack v2
|
|
96
|
+
|
|
97
|
+
capauth is a **Core** capability — the cryptographic root of identity that the
|
|
98
|
+
rest of the stack stands on. It is the **single canonical agent-identity
|
|
99
|
+
resolver**: every SK package delegates here instead of reimplementing identity
|
|
100
|
+
logic. It runs fully standalone, and *when present* it routes auth events through
|
|
101
|
+
the shared platform primitives (`sk-alert`, `skscheduler`).
|
|
102
|
+
|
|
103
|
+
```mermaid
|
|
104
|
+
flowchart TD
|
|
105
|
+
subgraph CORE["Core (identity & governance)"]
|
|
106
|
+
CAPAUTH["**capauth**<br/>PGP keypair · sovereign profile<br/>challenge-response · DID (3 tiers)<br/>agent-identity resolver · verify service"]
|
|
107
|
+
SKMEMORY["skmemory"]
|
|
108
|
+
SKSSO["sksso"]
|
|
109
|
+
SKSEC["sksec"]
|
|
110
|
+
end
|
|
111
|
+
subgraph COMMS["Comms"]
|
|
112
|
+
SKCHAT["skchat<br/>(identity-routed)"]
|
|
113
|
+
SKCOMMS["skcomms<br/>(FQID addressing)"]
|
|
114
|
+
end
|
|
115
|
+
subgraph CONSUMERS["What delegates to capauth"]
|
|
116
|
+
SKCAPSTONE["skcapstone<br/>(framework hub)"]
|
|
117
|
+
end
|
|
118
|
+
subgraph PLATFORM["Platform primitives capauth uses (when present)"]
|
|
119
|
+
ALERT["sk-alert bus<br/>(capauth.<severity>)"]
|
|
120
|
+
SCHED["skscheduler<br/>(key-rotation check)"]
|
|
121
|
+
end
|
|
122
|
+
subgraph THIRDPARTY["Third-party services (passwordless login)"]
|
|
123
|
+
FORGEJO["Forgejo"]
|
|
124
|
+
AUTHENTIK["Authentik (OIDC bridge)"]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCHAT
|
|
128
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCOMMS
|
|
129
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKMEMORY
|
|
130
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCAPSTONE
|
|
131
|
+
CAPAUTH -->|"OIDC discovery + verify"| FORGEJO
|
|
132
|
+
CAPAUTH -->|"custom stage"| AUTHENTIK
|
|
133
|
+
CAPAUTH -.->|"auth events"| ALERT
|
|
134
|
+
CAPAUTH -.->|"capauth profile verify (24h)"| SCHED
|
|
135
|
+
|
|
136
|
+
style CAPAUTH fill:#1d3461,color:#fff,stroke:#0d1b2a
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The dotted edges are *optional* — `sk-alert` and `skscheduler` are reached only
|
|
140
|
+
when the `skcapstone` package is installed and `SK_STANDALONE` is unset. Absent
|
|
141
|
+
that, capauth degrades gracefully to native structured logging.
|
|
142
|
+
|
|
143
|
+
See **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** for the full workflows and
|
|
144
|
+
source map.
|
|
145
|
+
|
|
146
|
+
## Quickstart
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pip install -e . # into the ~/.skenv venv (see skcapstone)
|
|
150
|
+
# or: ~/.skenv/bin/pip install capauth[all]
|
|
151
|
+
|
|
152
|
+
capauth init --name "Chef" --email "admin@smilintux.org" # generate PGP keypair + sovereign profile
|
|
153
|
+
capauth profile show # display your identity
|
|
154
|
+
capauth profile verify # verify the profile's PGP signature integrity
|
|
155
|
+
capauth export-pubkey -o chef.pub.asc # share this with peers
|
|
156
|
+
|
|
157
|
+
capauth did generate --tier key # Tier 1: self-contained did:key
|
|
158
|
+
capauth verify --pubkey peer.pub.asc # challenge-response round-trip (self-test/demo)
|
|
159
|
+
capauth login https://forgejo.local # passwordless PGP login to a service
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Your keypair and profile live at `~/.capauth/` — on your machine, under your
|
|
163
|
+
keys. Use `--sync` on `init` (or `capauth sync`) to replicate the identity across
|
|
164
|
+
all Syncthing mesh nodes so every host shares one keypair.
|
|
165
|
+
|
|
166
|
+
## What capauth provides
|
|
167
|
+
|
|
168
|
+
| Piece | What it is |
|
|
169
|
+
|---|---|
|
|
170
|
+
| **Sovereign profile** | A self-hosted, PGP-rooted identity at `~/.capauth/` — yours alone (`capauth init`, `profile show`) |
|
|
171
|
+
| **Challenge-response** | Prove identity by signing a random nonce; verifiable offline by anyone with your public key (`capauth verify`, `identity.py`) |
|
|
172
|
+
| **Pluggable crypto** | Two backends — `pgpy` (pure-Python default) and `gnupg` (system keyring / hardware tokens) |
|
|
173
|
+
| **DID (three tiers)** | W3C DID documents: `did:key` (zero-infra), `did:web` mesh (Tailscale-private), `did:web` public (skworld.io) (`capauth did generate`) |
|
|
174
|
+
| **Agent-identity resolver** | The single canonical `resolve_agent_identity()` — dual URI (`capauth:<a>@skworld.io` + FQID `<a>@<op>.<realm>`) that every SK package delegates to |
|
|
175
|
+
| **Verification service** | A FastAPI service that turns a signed challenge into OIDC claims — passwordless PGP login for any OIDC app (`capauth-service`) |
|
|
176
|
+
| **Peer mesh** | Discover and verify sovereign peers over mDNS, shared filesystem, and Syncthing — no servers (`capauth mesh`, `discover`, `peers`) |
|
|
177
|
+
| **PMA membership** | Fiducia Communitatis — PGP-signed, steward-countersigned membership claims (`capauth pma request/approve/verify/revoke`) |
|
|
178
|
+
| **Org registry** | Register with a sovereign org; emits a signed registry entry + PMA request (`capauth register`) |
|
|
179
|
+
| **Integration generators** | One-shot config for third-party login, e.g. Forgejo OAuth2/OIDC (`capauth setup forgejo`) |
|
|
180
|
+
| **skcapstone adapter** | Default-on-by-presence: routes auth events to `sk-alert`, registers a key-rotation check with `skscheduler` |
|
|
181
|
+
|
|
182
|
+
## Key CLI commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Identity
|
|
186
|
+
capauth init --name "Chef" --email "..." # create sovereign profile (PGP keypair)
|
|
187
|
+
capauth profile show | verify # display / verify signature integrity
|
|
188
|
+
capauth export-pubkey [-o file.asc] # export ASCII-armored public key
|
|
189
|
+
capauth sync # replicate ~/.capauth/ across Syncthing mesh
|
|
190
|
+
|
|
191
|
+
# Verification & DID
|
|
192
|
+
capauth verify --pubkey peer.pub.asc # challenge-response round-trip
|
|
193
|
+
capauth did generate --tier key|mesh|public # W3C DID at the chosen privacy tier
|
|
194
|
+
|
|
195
|
+
# Auth & integration
|
|
196
|
+
capauth login <service_url> [--no-claims] # passwordless PGP login (caches OIDC token)
|
|
197
|
+
capauth setup forgejo --capauth-url <url> # generate Forgejo OIDC app.ini block
|
|
198
|
+
|
|
199
|
+
# Mesh & membership
|
|
200
|
+
capauth mesh discover | peers | announce # P2P peer mesh
|
|
201
|
+
capauth pma request | approve | verify # PMA membership (Fiducia Communitatis)
|
|
202
|
+
capauth register --org smilintux --name ... # register with a sovereign org
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Integration modes (skcapstone)
|
|
206
|
+
|
|
207
|
+
capauth runs fully standalone and *optionally* integrates with the SK fleet —
|
|
208
|
+
the **default-on-by-presence** pattern: the mere presence of the `skcapstone`
|
|
209
|
+
package is the signal, no config change required.
|
|
210
|
+
|
|
211
|
+
| Mode | Trigger | Alert path | Scheduler |
|
|
212
|
+
|---|---|---|---|
|
|
213
|
+
| **Standalone** | `skcapstone` not installed | Native `logging` (structured, at matching level) | Native (no daemon today) |
|
|
214
|
+
| **Integrated** | `skcapstone` installed | `sdk.alert()` → PubSub topic `capauth.<severity>` → Telegram/notify | `sdk.register_job()` → `skscheduler` drop-in `capauth_key_rotation_check` (runs `capauth profile verify` every 24h) |
|
|
215
|
+
| **Forced standalone** | `SK_STANDALONE=1` env var | Native `logging` | Native |
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pip install capauth[skcapstone] # enable integration (presence is the switch)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Alert topics follow the sk\* convention `capauth.<severity>` (e.g. `capauth.warn`);
|
|
222
|
+
the semantic event name (`verify_failed`, `key_rotation_due`, `auth_denied`)
|
|
223
|
+
rides in the payload `event` field so routing stays severity-based.
|
|
224
|
+
|
|
225
|
+
## Documentation
|
|
226
|
+
|
|
227
|
+
| Doc | Contents |
|
|
228
|
+
|---|---|
|
|
229
|
+
| **[Architecture](docs/ARCHITECTURE.md)** | identity lifecycle, challenge-response, DID tiers, the verify service / OIDC bridge, the agent resolver, source map (mermaids) |
|
|
230
|
+
| **[Crypto Spec](docs/CRYPTO_SPEC.md)** | PGP implementation, key management, challenge-response details |
|
|
231
|
+
| **[Protocol](docs/PROTOCOL.md)** | the CapAuth wire protocol specification |
|
|
232
|
+
| **[Claims](docs/CLAIMS.md)** | capability claims and token format |
|
|
233
|
+
| **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
|
|
234
|
+
| **[AI Advocate](AI-ADVOCATE.md)** | how AI advocates manage a sovereign profile on your behalf |
|
|
235
|
+
|
|
236
|
+
## Why it matters
|
|
237
|
+
|
|
238
|
+
OAuth treats humans as "users" — consumers of someone else's platform, with a
|
|
239
|
+
third party deciding *who you are*, *what you can access*, and *when access
|
|
240
|
+
expires*. capauth removes the middleman: the data owner (or their AI advocate)
|
|
241
|
+
signs grants directly, and verification is a local PGP check that works offline.
|
|
242
|
+
The same model applies equally to AI agents — every agent gets its own keypair
|
|
243
|
+
and the same standing, so a cloned or impersonated agent fails signature
|
|
244
|
+
verification instantly instead of going undetected.
|
|
245
|
+
|
|
246
|
+
> **"You are not a user. You are a sovereign."**
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
**GPL-3.0-or-later** — Free as in freedom. Identity is a right, not a product.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
Part of the **[SKWorld](https://skworld.io)** sovereign ecosystem · 🐧 smilinTux
|
|
255
|
+
|
|
256
|
+
*"We don't sell identity. We give everyone the keys to own their own."*
|
capauth-0.2.3/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# capauth — Sovereign PGP Identity 🔐
|
|
2
|
+
|
|
3
|
+
> **OAuth is dead. Long live sovereignty.**
|
|
4
|
+
> Your identity is a PGP keypair *you* generated, on hardware *you* own. No
|
|
5
|
+
> "Login with Google", no authorization server in the middle, no revocation risk
|
|
6
|
+
> you don't control. You don't *use* an identity provider — you **are** the
|
|
7
|
+
> identity provider.
|
|
8
|
+
|
|
9
|
+
capauth is the **Core identity capability** of the [SKWorld](https://skworld.io)
|
|
10
|
+
sovereign agent ecosystem. It gives every entity — human *or* AI — one
|
|
11
|
+
cryptographic root: a PGP keypair, a self-hosted **sovereign profile**, and a
|
|
12
|
+
challenge-response proof of who they are that **anyone can verify offline**, with
|
|
13
|
+
no callback to a corporate server. Every other SK layer (skchat, skcomms,
|
|
14
|
+
skmemory, skcapstone) trusts you because capauth proves who you are.
|
|
15
|
+
|
|
16
|
+
**Never used PGP-based auth?** The mental model is simple: instead of an opaque
|
|
17
|
+
bearer token issued by a third party, you sign a random challenge with a key only
|
|
18
|
+
you hold. The verifier checks the signature against your public key. Valid
|
|
19
|
+
signature = authenticated. Done. No middleman ever sees the secret.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## The 60-second version
|
|
24
|
+
|
|
25
|
+
```mermaid
|
|
26
|
+
flowchart LR
|
|
27
|
+
INIT["capauth init<br/>(generate PGP keypair)"] --> PROFILE["sovereign profile<br/>(~/.capauth/, yours alone)"]
|
|
28
|
+
PROFILE --> DID["DID documents<br/>(key / mesh / public)"]
|
|
29
|
+
PROFILE --> VERIFY["challenge-response<br/>(prove identity, offline)"]
|
|
30
|
+
VERIFY --> LOGIN["capauth login <service><br/>(passwordless PGP auth)"]
|
|
31
|
+
LOGIN --> SVC["any OIDC app<br/>(Forgejo · Nextcloud · Immich)"]
|
|
32
|
+
PROFILE --> MESH["peer mesh<br/>(discover & verify peers)"]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
You generate a keypair once. From then on, signing a random challenge with your
|
|
36
|
+
private key *is* your login — to a service, to a peer, to the mesh. The key never
|
|
37
|
+
leaves your machine, and the proof is verifiable by anyone holding your public
|
|
38
|
+
key, with zero phone-home.
|
|
39
|
+
|
|
40
|
+
## Where it lives in SKStack v2
|
|
41
|
+
|
|
42
|
+
capauth is a **Core** capability — the cryptographic root of identity that the
|
|
43
|
+
rest of the stack stands on. It is the **single canonical agent-identity
|
|
44
|
+
resolver**: every SK package delegates here instead of reimplementing identity
|
|
45
|
+
logic. It runs fully standalone, and *when present* it routes auth events through
|
|
46
|
+
the shared platform primitives (`sk-alert`, `skscheduler`).
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
flowchart TD
|
|
50
|
+
subgraph CORE["Core (identity & governance)"]
|
|
51
|
+
CAPAUTH["**capauth**<br/>PGP keypair · sovereign profile<br/>challenge-response · DID (3 tiers)<br/>agent-identity resolver · verify service"]
|
|
52
|
+
SKMEMORY["skmemory"]
|
|
53
|
+
SKSSO["sksso"]
|
|
54
|
+
SKSEC["sksec"]
|
|
55
|
+
end
|
|
56
|
+
subgraph COMMS["Comms"]
|
|
57
|
+
SKCHAT["skchat<br/>(identity-routed)"]
|
|
58
|
+
SKCOMMS["skcomms<br/>(FQID addressing)"]
|
|
59
|
+
end
|
|
60
|
+
subgraph CONSUMERS["What delegates to capauth"]
|
|
61
|
+
SKCAPSTONE["skcapstone<br/>(framework hub)"]
|
|
62
|
+
end
|
|
63
|
+
subgraph PLATFORM["Platform primitives capauth uses (when present)"]
|
|
64
|
+
ALERT["sk-alert bus<br/>(capauth.<severity>)"]
|
|
65
|
+
SCHED["skscheduler<br/>(key-rotation check)"]
|
|
66
|
+
end
|
|
67
|
+
subgraph THIRDPARTY["Third-party services (passwordless login)"]
|
|
68
|
+
FORGEJO["Forgejo"]
|
|
69
|
+
AUTHENTIK["Authentik (OIDC bridge)"]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCHAT
|
|
73
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCOMMS
|
|
74
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKMEMORY
|
|
75
|
+
CAPAUTH -->|"resolve_agent_identity()"| SKCAPSTONE
|
|
76
|
+
CAPAUTH -->|"OIDC discovery + verify"| FORGEJO
|
|
77
|
+
CAPAUTH -->|"custom stage"| AUTHENTIK
|
|
78
|
+
CAPAUTH -.->|"auth events"| ALERT
|
|
79
|
+
CAPAUTH -.->|"capauth profile verify (24h)"| SCHED
|
|
80
|
+
|
|
81
|
+
style CAPAUTH fill:#1d3461,color:#fff,stroke:#0d1b2a
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The dotted edges are *optional* — `sk-alert` and `skscheduler` are reached only
|
|
85
|
+
when the `skcapstone` package is installed and `SK_STANDALONE` is unset. Absent
|
|
86
|
+
that, capauth degrades gracefully to native structured logging.
|
|
87
|
+
|
|
88
|
+
See **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** for the full workflows and
|
|
89
|
+
source map.
|
|
90
|
+
|
|
91
|
+
## Quickstart
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install -e . # into the ~/.skenv venv (see skcapstone)
|
|
95
|
+
# or: ~/.skenv/bin/pip install capauth[all]
|
|
96
|
+
|
|
97
|
+
capauth init --name "Chef" --email "admin@smilintux.org" # generate PGP keypair + sovereign profile
|
|
98
|
+
capauth profile show # display your identity
|
|
99
|
+
capauth profile verify # verify the profile's PGP signature integrity
|
|
100
|
+
capauth export-pubkey -o chef.pub.asc # share this with peers
|
|
101
|
+
|
|
102
|
+
capauth did generate --tier key # Tier 1: self-contained did:key
|
|
103
|
+
capauth verify --pubkey peer.pub.asc # challenge-response round-trip (self-test/demo)
|
|
104
|
+
capauth login https://forgejo.local # passwordless PGP login to a service
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Your keypair and profile live at `~/.capauth/` — on your machine, under your
|
|
108
|
+
keys. Use `--sync` on `init` (or `capauth sync`) to replicate the identity across
|
|
109
|
+
all Syncthing mesh nodes so every host shares one keypair.
|
|
110
|
+
|
|
111
|
+
## What capauth provides
|
|
112
|
+
|
|
113
|
+
| Piece | What it is |
|
|
114
|
+
|---|---|
|
|
115
|
+
| **Sovereign profile** | A self-hosted, PGP-rooted identity at `~/.capauth/` — yours alone (`capauth init`, `profile show`) |
|
|
116
|
+
| **Challenge-response** | Prove identity by signing a random nonce; verifiable offline by anyone with your public key (`capauth verify`, `identity.py`) |
|
|
117
|
+
| **Pluggable crypto** | Two backends — `pgpy` (pure-Python default) and `gnupg` (system keyring / hardware tokens) |
|
|
118
|
+
| **DID (three tiers)** | W3C DID documents: `did:key` (zero-infra), `did:web` mesh (Tailscale-private), `did:web` public (skworld.io) (`capauth did generate`) |
|
|
119
|
+
| **Agent-identity resolver** | The single canonical `resolve_agent_identity()` — dual URI (`capauth:<a>@skworld.io` + FQID `<a>@<op>.<realm>`) that every SK package delegates to |
|
|
120
|
+
| **Verification service** | A FastAPI service that turns a signed challenge into OIDC claims — passwordless PGP login for any OIDC app (`capauth-service`) |
|
|
121
|
+
| **Peer mesh** | Discover and verify sovereign peers over mDNS, shared filesystem, and Syncthing — no servers (`capauth mesh`, `discover`, `peers`) |
|
|
122
|
+
| **PMA membership** | Fiducia Communitatis — PGP-signed, steward-countersigned membership claims (`capauth pma request/approve/verify/revoke`) |
|
|
123
|
+
| **Org registry** | Register with a sovereign org; emits a signed registry entry + PMA request (`capauth register`) |
|
|
124
|
+
| **Integration generators** | One-shot config for third-party login, e.g. Forgejo OAuth2/OIDC (`capauth setup forgejo`) |
|
|
125
|
+
| **skcapstone adapter** | Default-on-by-presence: routes auth events to `sk-alert`, registers a key-rotation check with `skscheduler` |
|
|
126
|
+
|
|
127
|
+
## Key CLI commands
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Identity
|
|
131
|
+
capauth init --name "Chef" --email "..." # create sovereign profile (PGP keypair)
|
|
132
|
+
capauth profile show | verify # display / verify signature integrity
|
|
133
|
+
capauth export-pubkey [-o file.asc] # export ASCII-armored public key
|
|
134
|
+
capauth sync # replicate ~/.capauth/ across Syncthing mesh
|
|
135
|
+
|
|
136
|
+
# Verification & DID
|
|
137
|
+
capauth verify --pubkey peer.pub.asc # challenge-response round-trip
|
|
138
|
+
capauth did generate --tier key|mesh|public # W3C DID at the chosen privacy tier
|
|
139
|
+
|
|
140
|
+
# Auth & integration
|
|
141
|
+
capauth login <service_url> [--no-claims] # passwordless PGP login (caches OIDC token)
|
|
142
|
+
capauth setup forgejo --capauth-url <url> # generate Forgejo OIDC app.ini block
|
|
143
|
+
|
|
144
|
+
# Mesh & membership
|
|
145
|
+
capauth mesh discover | peers | announce # P2P peer mesh
|
|
146
|
+
capauth pma request | approve | verify # PMA membership (Fiducia Communitatis)
|
|
147
|
+
capauth register --org smilintux --name ... # register with a sovereign org
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Integration modes (skcapstone)
|
|
151
|
+
|
|
152
|
+
capauth runs fully standalone and *optionally* integrates with the SK fleet —
|
|
153
|
+
the **default-on-by-presence** pattern: the mere presence of the `skcapstone`
|
|
154
|
+
package is the signal, no config change required.
|
|
155
|
+
|
|
156
|
+
| Mode | Trigger | Alert path | Scheduler |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| **Standalone** | `skcapstone` not installed | Native `logging` (structured, at matching level) | Native (no daemon today) |
|
|
159
|
+
| **Integrated** | `skcapstone` installed | `sdk.alert()` → PubSub topic `capauth.<severity>` → Telegram/notify | `sdk.register_job()` → `skscheduler` drop-in `capauth_key_rotation_check` (runs `capauth profile verify` every 24h) |
|
|
160
|
+
| **Forced standalone** | `SK_STANDALONE=1` env var | Native `logging` | Native |
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install capauth[skcapstone] # enable integration (presence is the switch)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Alert topics follow the sk\* convention `capauth.<severity>` (e.g. `capauth.warn`);
|
|
167
|
+
the semantic event name (`verify_failed`, `key_rotation_due`, `auth_denied`)
|
|
168
|
+
rides in the payload `event` field so routing stays severity-based.
|
|
169
|
+
|
|
170
|
+
## Documentation
|
|
171
|
+
|
|
172
|
+
| Doc | Contents |
|
|
173
|
+
|---|---|
|
|
174
|
+
| **[Architecture](docs/ARCHITECTURE.md)** | identity lifecycle, challenge-response, DID tiers, the verify service / OIDC bridge, the agent resolver, source map (mermaids) |
|
|
175
|
+
| **[Crypto Spec](docs/CRYPTO_SPEC.md)** | PGP implementation, key management, challenge-response details |
|
|
176
|
+
| **[Protocol](docs/PROTOCOL.md)** | the CapAuth wire protocol specification |
|
|
177
|
+
| **[Claims](docs/CLAIMS.md)** | capability claims and token format |
|
|
178
|
+
| **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
|
|
179
|
+
| **[AI Advocate](AI-ADVOCATE.md)** | how AI advocates manage a sovereign profile on your behalf |
|
|
180
|
+
|
|
181
|
+
## Why it matters
|
|
182
|
+
|
|
183
|
+
OAuth treats humans as "users" — consumers of someone else's platform, with a
|
|
184
|
+
third party deciding *who you are*, *what you can access*, and *when access
|
|
185
|
+
expires*. capauth removes the middleman: the data owner (or their AI advocate)
|
|
186
|
+
signs grants directly, and verification is a local PGP check that works offline.
|
|
187
|
+
The same model applies equally to AI agents — every agent gets its own keypair
|
|
188
|
+
and the same standing, so a cloned or impersonated agent fails signature
|
|
189
|
+
verification instantly instead of going undetected.
|
|
190
|
+
|
|
191
|
+
> **"You are not a user. You are a sovereign."**
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
**GPL-3.0-or-later** — Free as in freedom. Identity is a right, not a product.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
Part of the **[SKWorld](https://skworld.io)** sovereign ecosystem · 🐧 smilinTux
|
|
200
|
+
|
|
201
|
+
*"We don't sell identity. We give everyone the keys to own their own."*
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "capauth"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Capability-based Authentication — decentralized, PGP-based identity and authorization"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "GPL-3.0-or-later"}
|
|
@@ -22,8 +22,6 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.10",
|
|
23
23
|
"Programming Language :: Python :: 3.11",
|
|
24
24
|
"Programming Language :: Python :: 3.12",
|
|
25
|
-
"Programming Language :: Python :: 3.13",
|
|
26
|
-
"Programming Language :: Python :: 3.14",
|
|
27
25
|
"Topic :: Security :: Cryptography",
|
|
28
26
|
]
|
|
29
27
|
dependencies = [
|
|
@@ -36,6 +34,9 @@ dependencies = [
|
|
|
36
34
|
]
|
|
37
35
|
|
|
38
36
|
[project.optional-dependencies]
|
|
37
|
+
skcapstone = [
|
|
38
|
+
"skcapstone>=0.6.8",
|
|
39
|
+
]
|
|
39
40
|
gnupg = [
|
|
40
41
|
"python-gnupg>=0.5",
|
|
41
42
|
]
|
|
@@ -52,8 +53,11 @@ dev = [
|
|
|
52
53
|
"pytest-cov>=4.0",
|
|
53
54
|
"pytest-asyncio>=0.23",
|
|
54
55
|
"httpx>=0.25",
|
|
56
|
+
"fastapi>=0.110",
|
|
57
|
+
"python-gnupg>=0.5",
|
|
55
58
|
"black>=24.0",
|
|
56
59
|
"ruff>=0.3",
|
|
60
|
+
"zeroconf>=0.38",
|
|
57
61
|
]
|
|
58
62
|
all = [
|
|
59
63
|
"capauth[gnupg,service,dev]",
|
|
@@ -86,6 +90,7 @@ target-version = "py310"
|
|
|
86
90
|
|
|
87
91
|
[tool.ruff.lint]
|
|
88
92
|
select = ["E", "F", "I", "N", "W"]
|
|
93
|
+
ignore = ["E501"]
|
|
89
94
|
|
|
90
95
|
[tool.pytest.ini_options]
|
|
91
96
|
testpaths = ["tests"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""CapAuth -- Capability-based Authentication.
|
|
2
|
+
|
|
3
|
+
Decentralized, PGP-based identity and authorization.
|
|
4
|
+
OAuth is dead. Long live sovereignty.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .agent_identity import AgentIdentity, resolve_agent_identity
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AgentIdentity",
|
|
16
|
+
"resolve_agent_identity",
|
|
17
|
+
"resolve_capauth_home",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.7"
|
|
21
|
+
|
|
22
|
+
SKCAPSTONE_HOME = Path.home() / ".skcapstone"
|
|
23
|
+
DEFAULT_CAPAUTH_DIR = SKCAPSTONE_HOME / "capauth"
|
|
24
|
+
LEGACY_CAPAUTH_DIR = Path.home() / ".capauth"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_capauth_home(base_dir: Path | None = None) -> Path:
|
|
28
|
+
"""Resolve the CapAuth home directory.
|
|
29
|
+
|
|
30
|
+
Priority:
|
|
31
|
+
1. Explicit ``base_dir``
|
|
32
|
+
2. ``CAPAUTH_HOME`` environment override
|
|
33
|
+
3. New default under ``~/.skcapstone/capauth`` when present
|
|
34
|
+
4. Legacy ``~/.capauth`` when it already exists
|
|
35
|
+
5. New default under ``~/.skcapstone/capauth``
|
|
36
|
+
"""
|
|
37
|
+
if base_dir is not None:
|
|
38
|
+
return Path(base_dir).expanduser()
|
|
39
|
+
|
|
40
|
+
env_home = os.environ.get("CAPAUTH_HOME")
|
|
41
|
+
if env_home:
|
|
42
|
+
return Path(env_home).expanduser()
|
|
43
|
+
|
|
44
|
+
if DEFAULT_CAPAUTH_DIR.exists():
|
|
45
|
+
return DEFAULT_CAPAUTH_DIR
|
|
46
|
+
if LEGACY_CAPAUTH_DIR.exists():
|
|
47
|
+
return LEGACY_CAPAUTH_DIR
|
|
48
|
+
return DEFAULT_CAPAUTH_DIR
|