pacific-solid 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.
- pacific_solid-0.1.0/.claude/settings.local.json +16 -0
- pacific_solid-0.1.0/.github/workflows/ci.yml +76 -0
- pacific_solid-0.1.0/.gitignore +36 -0
- pacific_solid-0.1.0/CLAUDE.md +21 -0
- pacific_solid-0.1.0/LICENSE +21 -0
- pacific_solid-0.1.0/PKG-INFO +389 -0
- pacific_solid-0.1.0/README.md +349 -0
- pacific_solid-0.1.0/docker-compose.yml +6 -0
- pacific_solid-0.1.0/docs/hackathon/index.html +699 -0
- pacific_solid-0.1.0/docs/hackathon/sovereign-modules-brief.html +318 -0
- pacific_solid-0.1.0/docs/hackathon/sovereign-modules-brief.md +156 -0
- pacific_solid-0.1.0/docs/protocol/n3-patch.md +68 -0
- pacific_solid-0.1.0/docs/protocol/solid-client-requirements.json +1333 -0
- pacific_solid-0.1.0/docs/protocol/solid-notifications.md +100 -0
- pacific_solid-0.1.0/docs/protocol/solid-oidc.md +52 -0
- pacific_solid-0.1.0/docs/protocol/solid-protocol.md +112 -0
- pacific_solid-0.1.0/docs/protocol/web-access-control.md +94 -0
- pacific_solid-0.1.0/docs/protocol/webid-profile.md +43 -0
- pacific_solid-0.1.0/docs/research/gov-federated-graphs-lit-review.md +120 -0
- pacific_solid-0.1.0/explore_protocol.py +510 -0
- pacific_solid-0.1.0/pacific_solid/__init__.py +175 -0
- pacific_solid-0.1.0/pacific_solid/_acl/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_acl/acp.py +47 -0
- pacific_solid-0.1.0/pacific_solid/_acl/grant.py +22 -0
- pacific_solid-0.1.0/pacific_solid/_acl/modes.py +8 -0
- pacific_solid-0.1.0/pacific_solid/_acl/wac.py +195 -0
- pacific_solid-0.1.0/pacific_solid/_auth/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_auth/auth_code.py +141 -0
- pacific_solid-0.1.0/pacific_solid/_auth/credentials.py +74 -0
- pacific_solid-0.1.0/pacific_solid/_auth/dpop.py +238 -0
- pacific_solid-0.1.0/pacific_solid/_auth/oidc.py +27 -0
- pacific_solid-0.1.0/pacific_solid/_auth/pod.py +285 -0
- pacific_solid-0.1.0/pacific_solid/_auth/session.py +240 -0
- pacific_solid-0.1.0/pacific_solid/_auth/token_validation.py +183 -0
- pacific_solid-0.1.0/pacific_solid/_discovery/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_discovery/storage.py +123 -0
- pacific_solid-0.1.0/pacific_solid/_graph/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_graph/graph.py +263 -0
- pacific_solid-0.1.0/pacific_solid/_graph/triple.py +68 -0
- pacific_solid-0.1.0/pacific_solid/_http/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_http/client.py +131 -0
- pacific_solid-0.1.0/pacific_solid/_http/errors.py +62 -0
- pacific_solid-0.1.0/pacific_solid/_http/headers.py +123 -0
- pacific_solid-0.1.0/pacific_solid/_http/tls.py +37 -0
- pacific_solid-0.1.0/pacific_solid/_identity/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_identity/webid.py +109 -0
- pacific_solid-0.1.0/pacific_solid/_ldn/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_ldn/inbox.py +180 -0
- pacific_solid-0.1.0/pacific_solid/_model/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_model/base.py +40 -0
- pacific_solid-0.1.0/pacific_solid/_model/decorator.py +238 -0
- pacific_solid-0.1.0/pacific_solid/_model/fields.py +36 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/discovery.py +138 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/message.py +111 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/safety.py +95 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/subscription.py +128 -0
- pacific_solid-0.1.0/pacific_solid/_notifications/websocket.py +61 -0
- pacific_solid-0.1.0/pacific_solid/_rdf/__init__.py +0 -0
- pacific_solid-0.1.0/pacific_solid/_rdf/namespaces.py +49 -0
- pacific_solid-0.1.0/pacific_solid/_rdf/parse.py +48 -0
- pacific_solid-0.1.0/pacific_solid/_rdf/patch.py +256 -0
- pacific_solid-0.1.0/pacific_solid/_rdf/serialize.py +50 -0
- pacific_solid-0.1.0/pacific_solid/py.typed +0 -0
- pacific_solid-0.1.0/people/__init__.py +11 -0
- pacific_solid-0.1.0/pyproject.toml +62 -0
- pacific_solid-0.1.0/ruff.toml +21 -0
- pacific_solid-0.1.0/tests/__init__.py +0 -0
- pacific_solid-0.1.0/tests/e2e/__init__.py +0 -0
- pacific_solid-0.1.0/tests/e2e/conftest.py +153 -0
- pacific_solid-0.1.0/tests/e2e/test_smoke.py +349 -0
- pacific_solid-0.1.0/tests/unit/__init__.py +0 -0
- pacific_solid-0.1.0/tests/unit/test_acp_detection.py +41 -0
- pacific_solid-0.1.0/tests/unit/test_auth_code.py +198 -0
- pacific_solid-0.1.0/tests/unit/test_dpop.py +163 -0
- pacific_solid-0.1.0/tests/unit/test_errors.py +71 -0
- pacific_solid-0.1.0/tests/unit/test_graph.py +198 -0
- pacific_solid-0.1.0/tests/unit/test_headers.py +100 -0
- pacific_solid-0.1.0/tests/unit/test_hostile_client.py +387 -0
- pacific_solid-0.1.0/tests/unit/test_hostile_server.py +473 -0
- pacific_solid-0.1.0/tests/unit/test_jsonld_parse.py +95 -0
- pacific_solid-0.1.0/tests/unit/test_ldn.py +258 -0
- pacific_solid-0.1.0/tests/unit/test_model.py +210 -0
- pacific_solid-0.1.0/tests/unit/test_namespaces.py +30 -0
- pacific_solid-0.1.0/tests/unit/test_notification_discovery.py +120 -0
- pacific_solid-0.1.0/tests/unit/test_notification_message.py +144 -0
- pacific_solid-0.1.0/tests/unit/test_notification_safety.py +75 -0
- pacific_solid-0.1.0/tests/unit/test_notification_subscribe.py +135 -0
- pacific_solid-0.1.0/tests/unit/test_patch_builder.py +58 -0
- pacific_solid-0.1.0/tests/unit/test_storage_discovery.py +226 -0
- pacific_solid-0.1.0/tests/unit/test_tls.py +34 -0
- pacific_solid-0.1.0/tests/unit/test_token_validation.py +302 -0
- pacific_solid-0.1.0/tests/unit/test_triple.py +106 -0
- pacific_solid-0.1.0/tests/unit/test_wac.py +111 -0
- pacific_solid-0.1.0/tests/unit/test_wac_groups_external.py +109 -0
- pacific_solid-0.1.0/tests/unit/test_wac_origin.py +136 -0
- pacific_solid-0.1.0/tests/unit/test_webid_extended.py +224 -0
- pacific_solid-0.1.0/tests/unit/test_where_clause.py +108 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:solid.github.io)",
|
|
5
|
+
"WebFetch(domain:solidproject.org)",
|
|
6
|
+
"Bash(python3 -m ruff check people/ tests/)",
|
|
7
|
+
"Bash(pip3 install:*)",
|
|
8
|
+
"Bash(python3 -m ruff check people/ tests/ --fix)",
|
|
9
|
+
"Bash(python3 -m pytest tests/unit/ -v --tb=short)",
|
|
10
|
+
"WebFetch(domain:scholar.google.com)",
|
|
11
|
+
"WebFetch(domain:www.tandfonline.com)",
|
|
12
|
+
"WebFetch(domain:link.springer.com)",
|
|
13
|
+
"WebFetch(domain:www.sciencedirect.com)"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.13"
|
|
17
|
+
- run: pip install ruff
|
|
18
|
+
- run: ruff check .
|
|
19
|
+
|
|
20
|
+
typecheck:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.13"
|
|
27
|
+
- run: pip install -e ".[dev]"
|
|
28
|
+
- run: mypy people/ --ignore-missing-imports
|
|
29
|
+
|
|
30
|
+
unit:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
- run: pip install -e ".[dev]"
|
|
41
|
+
- run: pytest tests/unit/ -v
|
|
42
|
+
|
|
43
|
+
e2e:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
services:
|
|
46
|
+
css:
|
|
47
|
+
image: solidproject/community-server:latest
|
|
48
|
+
ports:
|
|
49
|
+
- 3000:3000
|
|
50
|
+
options: >-
|
|
51
|
+
--health-cmd "curl -f http://localhost:3000/ || exit 1"
|
|
52
|
+
--health-interval 5s
|
|
53
|
+
--health-timeout 5s
|
|
54
|
+
--health-retries 10
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
- uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.13"
|
|
60
|
+
- run: pip install -e ".[dev]"
|
|
61
|
+
- run: pytest tests/e2e/ -v
|
|
62
|
+
|
|
63
|
+
publish:
|
|
64
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
65
|
+
needs: [lint, typecheck, unit, e2e]
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
permissions:
|
|
68
|
+
id-token: write
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
- uses: actions/setup-python@v5
|
|
72
|
+
with:
|
|
73
|
+
python-version: "3.13"
|
|
74
|
+
- run: pip install build
|
|
75
|
+
- run: python -m build
|
|
76
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
# Python
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*$py.class
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.eggs/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
ENV/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Testing
|
|
28
|
+
.pytest_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
htmlcov/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.gopacific
|
|
33
|
+
|
|
34
|
+
# Distribution
|
|
35
|
+
*.tar.gz
|
|
36
|
+
*.whl
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# people
|
|
2
|
+
|
|
3
|
+
Python SDK for the Solid Project (solidproject.org). Open source, MIT license.
|
|
4
|
+
|
|
5
|
+
## Skill routing
|
|
6
|
+
|
|
7
|
+
When the user's request matches an available skill, ALWAYS invoke it using the Skill
|
|
8
|
+
tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
|
|
9
|
+
The skill has specialized workflows that produce better results than ad-hoc answers.
|
|
10
|
+
|
|
11
|
+
Key routing rules:
|
|
12
|
+
- Product ideas, "is this worth building", brainstorming → invoke office-hours
|
|
13
|
+
- Bugs, errors, "why is this broken", 500 errors → invoke investigate
|
|
14
|
+
- Ship, deploy, push, create PR → invoke ship
|
|
15
|
+
- QA, test the site, find bugs → invoke qa
|
|
16
|
+
- Code review, check my diff → invoke review
|
|
17
|
+
- Update docs after shipping → invoke document-release
|
|
18
|
+
- Weekly retro → invoke retro
|
|
19
|
+
- Design system, brand → invoke design-consultation
|
|
20
|
+
- Visual audit, design polish → invoke design-review
|
|
21
|
+
- Architecture review → invoke plan-eng-review
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2026 Pacific
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pacific-solid
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The Python SDK for the Solid Project
|
|
5
|
+
Project-URL: Homepage, https://github.com/Pacific-Systems-Ltd/people
|
|
6
|
+
Project-URL: Issues, https://github.com/Pacific-Systems-Ltd/people/issues
|
|
7
|
+
Author: Pacific
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: cryptography>=42.0
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: pyjwt[crypto]>=2.8
|
|
23
|
+
Requires-Dist: rdflib>=7.0
|
|
24
|
+
Requires-Dist: websockets>=13.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: networkx>=3.0; extra == 'all'
|
|
27
|
+
Requires-Dist: pandas>=2.0; extra == 'all'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: hypothesis>=6.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
35
|
+
Provides-Extra: pandas
|
|
36
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
37
|
+
Provides-Extra: science
|
|
38
|
+
Requires-Dist: networkx>=3.0; extra == 'science'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
<p align="right">
|
|
42
|
+
<img src="https://solidproject.org/assets/img/solid-emblem.svg" alt="Solid logo" height="120">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
# pacific-solid
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/pacific-solid/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
[](https://www.python.org/downloads/)
|
|
50
|
+
[](https://solidproject.org/TR/protocol)
|
|
51
|
+
|
|
52
|
+
**The Python SDK for the [Solid Project](https://solidproject.org). From [gopacific.ai](www.gopacific.ai).**
|
|
53
|
+
|
|
54
|
+
[Python](https://www.python.org) is the world's most popular programming language: the lingua franca of machine learning and data science. [Solid](https://solidproject.org/about) is [Tim Berners-Lee](https://en.wikipedia.org/wiki/Tim_Berners-Lee)'s architecture for a world where people control their own data. Created by the inventor of the [World Wide Web](https://en.wikipedia.org/wiki/World_Wide_Web), now stewarded by the [Open Data Institute](https://theodi.org/). The pacific-solid SDK connects them together.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## What you can build with this
|
|
58
|
+
|
|
59
|
+
### Citizen Digital Sovereignty
|
|
60
|
+
|
|
61
|
+
6.5 million Belgian citizens control their data with Solid. [Athumi](https://www.inrupt.com/case-study/flanders-strengthens-trusted-data-economy) was created by the Flemish government as Europe's first data utility company: a feduciary for the selective disclosure of personal data to government, employers, and businesses.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# 1. Applicant grants Randstad read access to their diploma
|
|
65
|
+
await applicant_session.grant(
|
|
66
|
+
resource="https://pods.athumi.be/citizen-12345/credentials/diploma",
|
|
67
|
+
agent="https://randstad.be/solid/profile#id",
|
|
68
|
+
modes=[AccessMode.READ],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# 2. Randstad reads the credential from the applicant's pod
|
|
72
|
+
credential = await randstad_session.read(
|
|
73
|
+
"https://pods.athumi.be/citizen-12345/credentials/diploma",
|
|
74
|
+
VerifiedCredential,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# 3. Verify and act
|
|
78
|
+
if credential.issuer == "https://kuleuven.be" and credential.valid:
|
|
79
|
+
approve_application(candidate)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Randstad uses Solid to [digitise the validation process for academic records](https://www.bbc.co.uk/news/business-68286395). This removes friction in the job market, helping people find work more easily. Meanwhile, citizens remain in control of their data. With pacific-solid, Python developers have easy access to the same toolkit.
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### Patient record portability
|
|
86
|
+
|
|
87
|
+
The [NHS has piloted](https://www.janeirodigital.com/blog/janeiro-digital-at-solid-world-nhs-personal-health-stores-with-xform-health-and-solid/) patient-controlled health records on Solid, and the Solid community is actively working on FHIR RDF in pods. When patients move between services, their medical history should move with them. No data migration, no lost history, fewer patients slipping through the gaps.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
patient = await session.read(
|
|
91
|
+
"https://pods.nhs.uk/jane-doe/medical/summary",
|
|
92
|
+
PatientSummary,
|
|
93
|
+
)
|
|
94
|
+
patient.conditions.append(new_diagnosis)
|
|
95
|
+
await session.update(patient) # Patient controls who else can see this
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Python is the [dominant language](https://www.unosquare.com/blog/programming-languages-for-biotech-from-drug-discovery-ai-to-clinical-systems/) for health data science, clinical research, and ML. This SDK links those pipelines to the Solid ecosystem.
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### Solid is the bedrock of the gopacific sovereign graph
|
|
102
|
+
|
|
103
|
+
Solid is the core of [gopacific's](https://gopacific.ai) organisational intelligence engine. When users interact in a gopacific network, Solid's disclosure mechanics ensure that sensitive data remain under their owners' control. We built this SDK to unlock the power of the Solid protocol for the Python community.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## Getting started
|
|
107
|
+
|
|
108
|
+
pacific-solid requires [Python](https://www.python.org/downloads/) 3.11 or higher.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install pacific-solid
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Dependencies
|
|
115
|
+
|
|
116
|
+
| Package | Purpose |
|
|
117
|
+
|---------|---------|
|
|
118
|
+
| [httpx](https://www.python-httpx.org/) | Async HTTP client |
|
|
119
|
+
| [rdflib](https://rdflib.readthedocs.io/) | RDF parsing and serialization (wrapped internally, never exposed) |
|
|
120
|
+
| [PyJWT](https://pyjwt.readthedocs.io/) | DPoP proof-of-possession token generation |
|
|
121
|
+
| [cryptography](https://cryptography.io/) | EC key pair generation for DPoP |
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## Quick start
|
|
125
|
+
|
|
126
|
+
### 1. Start a Solid server
|
|
127
|
+
|
|
128
|
+
For local development, run the open-source [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer) in Docker:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
docker run --rm -d -p 3000:3000 solidproject/community-server:latest -b http://localhost:3000
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Open http://localhost:3000, sign up for an account, create a pod, and generate client credentials from the account page. In production, you would point at a hosted Solid provider like [solidcommunity.net](https://solidcommunity.net/) or your organization's own server.
|
|
135
|
+
|
|
136
|
+
### 2. Authenticate and explore a pod
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
import asyncio
|
|
140
|
+
import pacific_solid as ps
|
|
141
|
+
|
|
142
|
+
async def main():
|
|
143
|
+
# Login once, reuse. ps = "personal store"
|
|
144
|
+
me = await ps.login(
|
|
145
|
+
issuer="http://localhost:3000",
|
|
146
|
+
client_id="YOUR_CLIENT_ID",
|
|
147
|
+
client_secret="YOUR_CLIENT_SECRET",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Scope to a pod for relative-path access
|
|
151
|
+
alice = me.pod("http://localhost:3000/my-pod/")
|
|
152
|
+
|
|
153
|
+
# List everything in the pod
|
|
154
|
+
resources = await alice.list("")
|
|
155
|
+
for url in resources:
|
|
156
|
+
print(url)
|
|
157
|
+
|
|
158
|
+
asyncio.run(main())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Authentication handles the full [Solid-OIDC](https://solidproject.org/TR/oidc) flow with DPoP proof-of-possession automatically. Every subsequent request is authenticated. Tokens are refreshed transparently.
|
|
162
|
+
|
|
163
|
+
### 3. Define a model and work with data
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import pacific_solid as ps
|
|
167
|
+
from pacific_solid import SCHEMA
|
|
168
|
+
|
|
169
|
+
@ps.model
|
|
170
|
+
class Note:
|
|
171
|
+
rdf_type = SCHEMA.NoteDigitalDocument
|
|
172
|
+
title: str = ps.field(SCHEMA.name)
|
|
173
|
+
body: str = ps.field(SCHEMA.text)
|
|
174
|
+
tags: list[str] = ps.field(SCHEMA.keywords, multiple=True)
|
|
175
|
+
|
|
176
|
+
# Create a note in a pod
|
|
177
|
+
note = Note(title="Hello Solid", body="My first note from Python.", tags=["solid", "python"])
|
|
178
|
+
url = await alice.create("notes/", note.graph, slug="hello")
|
|
179
|
+
|
|
180
|
+
# Read it back as a typed Python object
|
|
181
|
+
note = Note.from_graph(await alice.read(url))
|
|
182
|
+
print(note.title) # "Hello Solid"
|
|
183
|
+
|
|
184
|
+
# Modify and patch (only changed fields are sent via N3 Patch)
|
|
185
|
+
note.tags.append("decentralized")
|
|
186
|
+
await alice.patch(url, note.graph)
|
|
187
|
+
|
|
188
|
+
# Delete
|
|
189
|
+
await alice.delete(url)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
No raw triples. No manual Turtle construction. Python objects in, Python objects out.
|
|
193
|
+
|
|
194
|
+
## Why Python needs Solid. Why Solid needs Python.
|
|
195
|
+
|
|
196
|
+
Solid stores data as [RDF](https://www.w3.org/RDF/), a graph-based data model built on open W3C standards. Python already has the richest ecosystem for working with graph data, linked data, and knowledge graphs through libraries like rdflib, NetworkX, and the scientific Python stack.
|
|
197
|
+
|
|
198
|
+
Until now, the Solid SDK ecosystem has been a [JavaScript monoculture](https://docs.inrupt.com/developer-tools/javascript/client-libraries/). Inrupt maintains production-grade JS/TS and Java SDKs. Every other language, Python included, has had either fragmented community efforts or nothing at all. Authentication alone (Solid-OIDC with DPoP) has been the barrier that kills non-JS implementations. The [Solid Community Forum](https://forum.solidproject.org/) has years of threads from Python developers who couldn't get past it.
|
|
199
|
+
|
|
200
|
+
pacific-solid changes that.
|
|
201
|
+
|
|
202
|
+
| Python brings to Solid | Solid brings to Python |
|
|
203
|
+
|----------------------|----------------------|
|
|
204
|
+
| The dominant language in health data, bioinformatics, and clinical pipelines | Patient-controlled health records that Python pipelines can read with consent |
|
|
205
|
+
| Data science and ML on structured, linked data | Personal data stores that never leave the user's control |
|
|
206
|
+
| The world's largest developer community | A W3C-backed protocol for reading and writing linked data across organizational boundaries |
|
|
207
|
+
| AI agent frameworks that need somewhere to store knowledge | Sovereign knowledge graphs that agents populate, users own, and teams share |
|
|
208
|
+
| Automation and scripting for backend services | Decentralized authentication that works across providers |
|
|
209
|
+
| Mature RDF tooling (rdflib, 2400+ stars, 914K weekly downloads) | A reason for that tooling to exist beyond academic research |
|
|
210
|
+
|
|
211
|
+
## Features
|
|
212
|
+
|
|
213
|
+
### Three concepts
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
import pacific_solid as ps # ps = "personal store"
|
|
217
|
+
|
|
218
|
+
me = await ps.login(...) # Session: who you are
|
|
219
|
+
alice = me.pod("https://pod/alice/") # Pod: what you're looking at
|
|
220
|
+
graph = await alice.read("notes/") # Graph: the data
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Session** is your authenticated identity. Login once, reuse for any pod.
|
|
224
|
+
**Pod** is a scoped view onto a remote pod with relative paths.
|
|
225
|
+
**Graph** is a set of triples you can query, convert, and diff.
|
|
226
|
+
|
|
227
|
+
### Five methods, five HTTP verbs
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
graph = await alice.read("notes/hello") # GET
|
|
231
|
+
await alice.write("notes/hello", graph) # PUT (full replace)
|
|
232
|
+
await alice.patch("notes/hello", note.graph) # PATCH (N3 Patch from diff)
|
|
233
|
+
url = await alice.create("notes/", graph) # POST (new resource)
|
|
234
|
+
await alice.delete("notes/hello") # DELETE
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
No auto-detection between PUT and PATCH. The SDK does what you ask, explicitly.
|
|
238
|
+
|
|
239
|
+
### Typed models (optional)
|
|
240
|
+
|
|
241
|
+
Define Python classes that map to RDF predicates. The `@ps.model` decorator adds graph-awareness and snapshot-based dirty tracking.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
@ps.model
|
|
245
|
+
class Person:
|
|
246
|
+
rdf_type = ps.FOAF.Person
|
|
247
|
+
name: str = ps.field(ps.FOAF.name)
|
|
248
|
+
email: str = ps.field(ps.FOAF.mbox)
|
|
249
|
+
knows: list[str] = ps.field(ps.FOAF.knows, multiple=True)
|
|
250
|
+
|
|
251
|
+
person = Person.from_graph(await alice.read("profile/card"))
|
|
252
|
+
person.name = "Alice Smith"
|
|
253
|
+
await alice.patch("profile/card", person.graph) # Patches only the name triple
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Missing fields return `None` for scalars, `[]` for lists. Type checking is strict by default and configurable per-read with `strict=False`.
|
|
257
|
+
|
|
258
|
+
### Access control
|
|
259
|
+
|
|
260
|
+
Permissions are just graphs at `.acl` URLs. Grant or revoke access for specific agents, groups, or the public.
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# Shorthand
|
|
264
|
+
await alice.grant("health/gp-records", agent=dr_patel, modes=[ps.Read])
|
|
265
|
+
await alice.revoke("health/gp-records", agent=dr_patel)
|
|
266
|
+
|
|
267
|
+
# Or read the ACL graph directly
|
|
268
|
+
acl = await alice.read(graph.acl_url)
|
|
269
|
+
for grant in acl.all(ps.Grant):
|
|
270
|
+
print(grant.agent, grant.modes)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### WebID discovery
|
|
274
|
+
|
|
275
|
+
Resolve a WebID URI to discover OIDC issuers and pod storage URLs.
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
profile = await me.resolve("https://pods.example/alice/profile/card#me")
|
|
279
|
+
print(profile.issuers) # ["https://solid-server.example/"]
|
|
280
|
+
print(profile.storages) # ["https://pods.example/alice/"]
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Graph converters
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
graph.to_dict() # always available
|
|
287
|
+
graph.to_dataframe() # pip install pacific-solid[pandas]
|
|
288
|
+
graph.to_networkx() # pip install pacific-solid[science]
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Debug logging
|
|
292
|
+
|
|
293
|
+
Every HTTP request, DPoP proof, token refresh, and retry is logged via Python's standard `logging` module.
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
import logging
|
|
297
|
+
logging.getLogger("pacific_solid").setLevel(logging.DEBUG)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Specifications
|
|
301
|
+
|
|
302
|
+
pacific-solid implements:
|
|
303
|
+
|
|
304
|
+
| Specification | Version | Coverage |
|
|
305
|
+
|--------------|---------|----------|
|
|
306
|
+
| [Solid Protocol](https://solidproject.org/TR/protocol) | 0.11 | CRUD, LDP Containers, Content Negotiation, N3 Patch |
|
|
307
|
+
| [Solid-OIDC](https://solidproject.org/TR/oidc) | Draft | Client credentials + DPoP |
|
|
308
|
+
| [Web Access Control](https://solid.github.io/web-access-control-spec/) | 1.0 | Read, write, inherited ACLs |
|
|
309
|
+
| [WebID Profile](https://solid.github.io/webid-profile/) | Draft | OIDC issuer and storage discovery |
|
|
310
|
+
|
|
311
|
+
ACP (Access Control Policy), Solid Notifications, and JSON-LD content negotiation are planned for future releases.
|
|
312
|
+
|
|
313
|
+
## Architecture
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
pacific_solid/
|
|
317
|
+
_auth/ Session, Pod, Solid-OIDC, DPoP proof generation + verification, credentials
|
|
318
|
+
_graph/ Graph, Triple, URI, Literal, dict/pandas/networkx converters
|
|
319
|
+
_rdf/ Turtle parse/serialize (rdflib wrapper), N3 Patch builder, namespace constants
|
|
320
|
+
_model/ @ps.model decorator, ps.field(), snapshot-based dirty tracking
|
|
321
|
+
_acl/ WAC evaluation, Grant model, access modes
|
|
322
|
+
_identity/ WebID profile resolution, OIDC issuer + storage discovery
|
|
323
|
+
_http/ Authenticated httpx client, Link/WAC-Allow/ETag header parsing, error hierarchy
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Leading underscores = private implementation. The public API is re-exported from `__init__.py`. Users import from `pacific_solid`, never from internal packages.
|
|
327
|
+
|
|
328
|
+
rdflib is used internally for RDF parsing and serialization but is never exposed in the public API. The abstraction layer allows the RDF backend to be swapped (e.g. to Oxigraph) without breaking changes.
|
|
329
|
+
|
|
330
|
+
## Development
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
git clone https://github.com/Pacific-Systems-Ltd/people.git
|
|
334
|
+
cd people
|
|
335
|
+
pip install -e ".[dev]"
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Running tests
|
|
339
|
+
|
|
340
|
+
Unit tests (including adversarial/hostile tests) run instantly with no external dependencies. E2E tests run against a real [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer) in Docker.
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# Unit tests (181 tests, ~2 seconds)
|
|
344
|
+
pytest tests/unit/
|
|
345
|
+
|
|
346
|
+
# E2E tests (requires Docker)
|
|
347
|
+
docker compose up -d
|
|
348
|
+
pytest tests/e2e/
|
|
349
|
+
|
|
350
|
+
# Everything
|
|
351
|
+
docker compose up -d
|
|
352
|
+
pytest
|
|
353
|
+
|
|
354
|
+
# Lint and type check
|
|
355
|
+
ruff check .
|
|
356
|
+
mypy pacific_solid/
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Test structure
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
tests/
|
|
363
|
+
unit/
|
|
364
|
+
test_triple.py URI, Literal, Triple primitives
|
|
365
|
+
test_graph.py Graph CRUD, query, snapshot, Turtle round-trip
|
|
366
|
+
test_model.py @ps.model, field mapping, dirty tracking, Grant model
|
|
367
|
+
test_dpop.py DPoP generation + verification round-trip
|
|
368
|
+
test_wac.py WAC evaluation logic
|
|
369
|
+
test_patch_builder.py N3 Patch construction
|
|
370
|
+
test_headers.py Link, WAC-Allow, ETag parsing
|
|
371
|
+
test_errors.py Error hierarchy, status code mapping
|
|
372
|
+
test_namespaces.py Vocabulary constants
|
|
373
|
+
test_hostile_server.py Malicious server responses, header attacks, auth manipulation
|
|
374
|
+
test_hostile_client.py DPoP forgery, WAC bypass, N3 Patch injection
|
|
375
|
+
e2e/
|
|
376
|
+
test_smoke.py Full CRUD + Patch + WAC + WebID against real CSS
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Feedback and contributions
|
|
380
|
+
|
|
381
|
+
Bug reports and feature requests are welcome on [GitHub Issues](https://github.com/Pacific-Systems-Ltd/people/issues).
|
|
382
|
+
|
|
383
|
+
For questions about the Solid ecosystem, see the [Solid Community Forum](https://forum.solidproject.org/) and the [Solid Protocol specification](https://solidproject.org/TR/protocol).
|
|
384
|
+
|
|
385
|
+
## License
|
|
386
|
+
|
|
387
|
+
pacific-solid is open source software [licensed under MIT](LICENSE).
|
|
388
|
+
|
|
389
|
+
Copyright 2026 Pacific.
|