epistery 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ # Plug in architecture
2
+
3
+ This module is a plugin. It provides a host with data-wallet services exposed through
4
+ host.tld/.well-known/epistery. Epistery will create wallets for both the client (browser)
5
+ and the server if not otherwise provided. It provides the foundation methods for creating,
6
+ validating and manipulating data-wallets.
7
+
8
+ Server code is available through web calls. The
9
+ client is implemented with a browser script available from client.js.
10
+
11
+ ## Structure
12
+ This is a typescript project build on express.
13
+
14
+ | path | description |
15
+ |------------------------|------------------------------------------------------------------------------------------------------------------|
16
+ | /index.mjs | Entry point harness |
17
+ | /client | Assets and scripts intended to be embedded in the browser page |
18
+ | /client/client.js | client-side counterpart to /epistery.ts. |
19
+ | /client/ethers.js | Core blockchain tools |
20
+ | /client/status.html | The template page rendered by /.well-known/epistery/status. This is the only human readable content presented by /.epistery |
21
+ | /client/witness.js | |
22
+ | /src | implementation |
23
+ | /src/controllers/ | Controllers provide discreet services usually behind a named route /.well-known/epistery/[controller] |
24
+ | /src/utils | Shared tools that assist the controllers |
25
+ | /src/utils/Aqua.ts | Aqua protocol implementation that underlies the data wallet implementation |
26
+ | /src/utils/Config.ts | Interface to the $HOME/.epistory/config.ini and dependent configuration data |
27
+ | /src/utils/types.ts | Typescript common schema |
28
+ | /src/utils/Utils.ts | (Not sure. Seems like these methods should be attached to something with a named purpose |
29
+ | /src/utils/index.ts | Import root for reach all utilities |
30
+ | /src/epistery.ts | Root class that is connected by the host |
31
+ | /test | A barebones host application to provide sample code and exercise the features |
32
+ | /default.ini | template configuration for initialising a new installation |
33
+
34
+ >NOTE: api.ts is left out. I propose that if we want a standalone generic implementation of the epistery plugin,
35
+ > it should be implemented in a separate repo.
36
+
37
+ ## Data Wallets
38
+ The core purpose of the epistery plugin is manage the creation and manipulation of data wallets. This manifests as api's
39
+ invoked by the browser and partner sites
40
+
41
+ All of the Data Wallet functionality is found in the in /src/controllers/DataWalletController, operating behind .well-known/epistery/data. Utils
42
+ is used for common cryto functionaility and other tools
43
+
44
+ ## Signing Wallets
45
+
46
+ ## Config File
47
+ System configuration is managed with ini files in $HOME/.epistery. The root defines config.ini which has system wide
48
+ settings. The default settings are captured in default.ini. Each domain that has been initialized will have a folder
49
+ with it's own config.ini file, as well as key files and other persistent settings.
50
+
51
+ The root config file is structured into the following sections
52
+
53
+ ```ini
54
+ [profile]
55
+ name=
56
+ email=
57
+ [ipfs]
58
+ url=https://rootz.digital/api/v0
59
+ [default.provider]
60
+ // When a domain is initialized, it defaults to this provide info which is subsequencly saved with the domain config.ini
61
+ chainId=
62
+ name=
63
+ rpc=
64
+ ```
65
+
66
+ A domain config file, like `$HOME/.epistery/mydomain.com/config.ini`, includes:
67
+
68
+ ```ini
69
+ [provider]
70
+ chainId=
71
+ name=
72
+ rpc=
73
+
74
+ [wallet]
75
+ address=
76
+ mnemonic=
77
+ publicKey=
78
+ privateKey=
79
+
80
+ [ssl]
81
+ key=
82
+ cert=
83
+ modified=
84
+ ```
package/CLI.md ADDED
@@ -0,0 +1,291 @@
1
+ # Epistery CLI
2
+
3
+ Command-line interface for Epistery authentication and requests.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Initialize a domain (creates wallet in ~/.epistery/{domain}/)
9
+ epistery initialize localhost
10
+
11
+ # Set as default
12
+ epistery set-default localhost
13
+
14
+ # Make authenticated requests (automatic key exchange on first use)
15
+ epistery curl https://wiki.rootz.global/wiki/Home
16
+ ```
17
+
18
+ ## Commands
19
+
20
+ ### `epistery initialize <domain>`
21
+
22
+ Initialize a domain with a new wallet. Creates `~/.epistery/{domain}/config.ini` with:
23
+ - Wallet (address, keys, mnemonic)
24
+ - Provider configuration (from `~/.epistery/config.ini` default)
25
+
26
+ ```bash
27
+ epistery initialize localhost
28
+ epistery initialize wiki.rootz.global
29
+ ```
30
+
31
+ ### `epistery curl [options] <url>`
32
+
33
+ Make authenticated HTTP request. Automatically performs key exchange on first use.
34
+
35
+ **Options:**
36
+ - `-w, --wallet <domain>` - Use specific domain (overrides default)
37
+ - `-X, --request <method>` - HTTP method (default: GET)
38
+ - `-d, --data <data>` - Request body data
39
+ - `-H, --header <header>` - Additional headers
40
+ - `-b, --bot` - Use bot auth header (default: session cookie)
41
+ - `-v, --verbose` - Show detailed output
42
+
43
+ **Examples:**
44
+ ```bash
45
+ # GET request (uses default domain)
46
+ epistery curl https://wiki.rootz.global/wiki/Home
47
+
48
+ # Use specific domain
49
+ epistery curl -w localhost https://localhost:4080/session/context
50
+
51
+ # POST request
52
+ epistery curl -X POST -d '{"title":"Test","body":"# Test"}' https://wiki.rootz.global/wiki/Test
53
+
54
+ # Bot mode (no session, sign per request)
55
+ epistery curl --bot https://wiki.rootz.global/session/context
56
+ ```
57
+
58
+ ### `epistery info [domain]`
59
+
60
+ Show domain information (wallet address, provider, session status).
61
+
62
+ ```bash
63
+ epistery info # Show default domain
64
+ epistery info localhost # Show specific domain
65
+ ```
66
+
67
+ ### `epistery set-default <domain>`
68
+
69
+ Set default domain for CLI operations.
70
+
71
+ ```bash
72
+ epistery set-default localhost
73
+ ```
74
+
75
+ ## Architecture
76
+
77
+ ### Domain-Based Configuration
78
+
79
+ Epistery CLI uses the same domain configuration system as the server:
80
+
81
+ ```
82
+ ~/.epistery/
83
+ ├── config.ini # Root config with [cli] section
84
+ │ └── [cli]
85
+ │ └── default_domain=localhost
86
+ ├── localhost/
87
+ │ ├── config.ini # Domain config with wallet & provider
88
+ │ └── session.json # Session cookie (auto-created)
89
+ └── wiki.rootz.global/
90
+ ├── config.ini
91
+ └── session.json
92
+ ```
93
+
94
+ ### Authentication Modes
95
+
96
+ **Session Cookie (Default):**
97
+ - Performs key exchange once, saves session cookie
98
+ - Subsequent requests use cookie
99
+ - Best for multiple requests to same server
100
+
101
+ **Bot Auth Header (`--bot`):**
102
+ - Signs each request individually
103
+ - No session management
104
+ - Best for distributed systems or one-off requests
105
+
106
+ ### Automatic Key Exchange
107
+
108
+ The `curl` command automatically performs key exchange when needed:
109
+
110
+ 1. Check for existing session in `~/.epistery/{domain}/session.json`
111
+ 2. If no session, perform key exchange with server
112
+ 3. Save session cookie for future requests
113
+ 4. Make the actual HTTP request
114
+
115
+ No manual "connect" step required!
116
+
117
+ ## Usage Patterns
118
+
119
+ ### Local Development
120
+
121
+ ```bash
122
+ # Initialize for local development
123
+ epistery initialize localhost
124
+ epistery set-default localhost
125
+
126
+ # Make requests
127
+ epistery curl https://localhost:4080/wiki/index
128
+ epistery curl -X PUT -d '{"title":"Test","body":"# Test"}' https://localhost:4080/wiki/Test
129
+ ```
130
+
131
+ ### Multiple Domains
132
+
133
+ ```bash
134
+ # Initialize multiple domains
135
+ epistery initialize localhost
136
+ epistery initialize wiki.rootz.global
137
+ epistery initialize staging.example.com
138
+
139
+ # Switch between them
140
+ epistery curl -w localhost https://localhost:4080/...
141
+ epistery curl -w wiki.rootz.global https://wiki.rootz.global/...
142
+ epistery curl -w staging.example.com https://staging.example.com/...
143
+
144
+ # Or set default and omit -w
145
+ epistery set-default wiki.rootz.global
146
+ epistery curl https://wiki.rootz.global/...
147
+ ```
148
+
149
+ ### Bot/Agent Applications
150
+
151
+ ```javascript
152
+ import { CliWallet } from 'epistery';
153
+
154
+ // Load domain wallet
155
+ const wallet = CliWallet.load('localhost'); // or CliWallet.load() for default
156
+
157
+ // Create bot auth header
158
+ const authHeader = await wallet.createBotAuthHeader();
159
+
160
+ // Make request
161
+ const response = await fetch('https://localhost:4080/wiki/Home', {
162
+ headers: { 'Authorization': authHeader }
163
+ });
164
+ ```
165
+
166
+ ## Configuration Files
167
+
168
+ ### Root Config (`~/.epistery/config.ini`)
169
+
170
+ ```ini
171
+ [profile]
172
+ name=
173
+ email=
174
+
175
+ [ipfs]
176
+ url=https://rootz.digital/api/v0
177
+
178
+ [default.provider]
179
+ chainId=420420422,
180
+ name=polkadot-hub-testnet
181
+ rpc=https://testnet-passet-hub-eth-rpc.polkadot.io
182
+
183
+ [cli]
184
+ default_domain=localhost
185
+ ```
186
+
187
+ ### Domain Config (`~/.epistery/{domain}/config.ini`)
188
+
189
+ ```ini
190
+ [domain]
191
+ domain=localhost
192
+
193
+ [wallet]
194
+ address=0x...
195
+ mnemonic=word word word...
196
+ publicKey=0x04...
197
+ privateKey=0x...
198
+
199
+ [provider]
200
+ chainId=420420422,
201
+ name=polkadot-hub-testnet
202
+ rpc=https://testnet-passet-hub-eth-rpc.polkadot.io
203
+ ```
204
+
205
+ ### Session File (`~/.epistery/{domain}/session.json`)
206
+
207
+ Auto-created by `epistery curl`:
208
+
209
+ ```json
210
+ {
211
+ "domain": "https://wiki.rootz.global",
212
+ "cookie": "session_token_here",
213
+ "authenticated": true,
214
+ "timestamp": "2025-01-10T12:00:00.000Z"
215
+ }
216
+ ```
217
+
218
+ ## Security
219
+
220
+ - Domain configs stored with 0600 permissions (user only)
221
+ - Private keys never transmitted (only signatures)
222
+ - Each domain has its own isolated wallet
223
+ - Session cookies saved securely per domain
224
+
225
+ ## Design Philosophy
226
+
227
+ The Epistery CLI uses a unified command structure with subcommands:
228
+ - ✅ Uses existing Epistery domain config system
229
+ - ✅ Consistent with server-side architecture
230
+ - ✅ Automatic key exchange (no manual connect step)
231
+ - ✅ Default domain support (less typing)
232
+ - ✅ Simpler mental model (domain = wallet)
233
+
234
+ ## Examples
235
+
236
+ ### Initialize and Use
237
+
238
+ ```bash
239
+ # Setup
240
+ epistery initialize localhost
241
+ epistery set-default localhost
242
+
243
+ # Use
244
+ epistery curl https://wiki.rootz.global/wiki/Home
245
+ epistery curl https://wiki.rootz.global/wiki/index
246
+ ```
247
+
248
+ ### Multiple Domains
249
+
250
+ ```bash
251
+ # Setup each environment
252
+ epistery initialize dev.example.com
253
+ epistery initialize staging.example.com
254
+ epistery initialize prod.example.com
255
+
256
+ # Use different environments
257
+ epistery curl -w dev.example.com https://dev.example.com/api/status
258
+ epistery curl -w staging.example.com https://staging.example.com/api/status
259
+ epistery curl -w prod.example.com https://prod.example.com/api/status
260
+ ```
261
+
262
+ ### Bot Mode
263
+
264
+ ```bash
265
+ # Bot mode doesn't need session, signs each request
266
+ epistery curl --bot https://wiki.rootz.global/session/context
267
+ epistery curl --bot -X POST -d '{"title":"Log","body":"# Entry"}' https://wiki.rootz.global/wiki/Log
268
+ ```
269
+
270
+ ## Troubleshooting
271
+
272
+ **"Domain not found or has no wallet"**
273
+ - Run `epistery initialize <domain>` first
274
+
275
+ **"Key exchange failed"**
276
+ - Check server is running and accessible
277
+ - Verify URL is correct
278
+ - Use `-v` flag for detailed output
279
+
280
+ **"Failed to obtain session cookie"**
281
+ - Server may not be setting cookies
282
+ - Try `--bot` mode instead
283
+ - Check server configuration
284
+
285
+ ## Integration
286
+
287
+ The CLI is designed to work with:
288
+ - **Rhonda** - Wiki with Epistery authentication
289
+ - **Any Epistery-enabled app** - Just initialize and curl!
290
+
291
+ Server-side apps should implement bot authentication handler (see Rhonda's account-server for example).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 rootz-global
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,69 @@
1
+ # MongoDB Object _id Field Order Sensitivity
2
+
3
+ ## The Problem
4
+
5
+ MongoDB treats object `_id` fields as **order-sensitive**. This means:
6
+
7
+ ```javascript
8
+ {_id: {a: "root", d: "Home"}} // Different from...
9
+ {_id: {d: "Home", a: "root"}} // ...this!
10
+ ```
11
+
12
+ These are considered **two different documents** even though they have the same fields and values.
13
+
14
+ ## The Impact
15
+
16
+ When doing a PUT/upsert on the wiki, if you get the field order wrong in the `_id`, you will:
17
+ - ✅ NOT update the existing document
18
+ - ❌ CREATE a duplicate document with a different `_id` field order
19
+ - The original document remains unchanged and continues to be displayed
20
+ - Your new document becomes an orphan
21
+
22
+ ## The Solution
23
+
24
+ **Always preserve the exact field order when constructing `_id` objects:**
25
+
26
+ ```javascript
27
+ // Correct for wiki doclets:
28
+ {_id: {d: "PageName", a: "accountName"}}
29
+
30
+ // WRONG - will create duplicate:
31
+ {_id: {a: "accountName", d: "PageName"}}
32
+ ```
33
+
34
+ ## How to Avoid This
35
+
36
+ 1. **Read first, update second**: Always GET the existing document first to see the exact `_id` structure
37
+ 2. **Preserve field order**: When constructing updates, maintain the exact field order from the original
38
+ 3. **Use string _id when possible**: Avoid compound object `_id` fields in new collections
39
+
40
+ ## Cleaning Up Duplicates
41
+
42
+ If you accidentally create a duplicate:
43
+
44
+ ```javascript
45
+ // Find duplicates
46
+ db.wiki.find({"_id.d": "PageName"})
47
+
48
+ // Delete the wrong one (check field order!)
49
+ db.wiki.deleteOne({_id: {a: "root", d: "PageName"}})
50
+ ```
51
+
52
+ ## Why This Design?
53
+
54
+ This is an arcane MongoDB behavior that's a source of time-wasting bugs. String `_id` fields would be much safer.
55
+
56
+ ## TODO: Fix in wiki-mixin
57
+
58
+ **Action item**: Normalize the `_id` field order in `@metric-im/wiki-mixin` API so clients never have to worry about this.
59
+
60
+ The wiki API should:
61
+ 1. Always construct `_id` as `{d: docName, a: accountName}` regardless of input order
62
+ 2. Normalize any incoming `_id` objects to this canonical order before MongoDB operations
63
+ 3. Document the canonical order in API docs
64
+
65
+ This would prevent this issue for all wiki hosts and clients.
66
+
67
+ ---
68
+
69
+ *Documented 2025-10-14 after accidentally creating a duplicate Home page*
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Epistery
2
+
3
+ _Epistemology is the study of knowledge. An Epistery, it follows, is a place share the knowledge of knowledge._
4
+
5
+ This project is open source middleware that provides websites and browsers a shared neutral space to identify and
6
+ verify the origin of data and conduct digital business. It inserts the blockchain as a witness and clerk for the mundane
7
+ business of clicking, tipping, stamping and cloaking, currently run by commercial web gatekeepers.
8
+
9
+ Epistery provides the primitive tools for creating and rendering data-wallets.
10
+
11
+ * /.well-known/epistery - json data presenting the signing identity/wallet of the site
12
+ * /.well-known/epistery/status - human version of the above, plus overview of the site's activity and interactive features like comments, ratings.
13
+ * /.well-known/epistery/data/* - data-wallet module api for mint, manipulate, render and delete
14
+ * /.well-known/acme - Ephemeral ACME url for authorizing ssl cert assignment.
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ npm install epistery
20
+ ```
21
+
22
+ Initialize your domain:
23
+ ```bash
24
+ npx epistery initialize mydomain.com
25
+ ```
26
+
27
+ In your Express application:
28
+ ```javascript
29
+ import express from 'express';
30
+ import https from 'https';
31
+ import { Epistery } from 'epistery';
32
+
33
+ const app = express();
34
+
35
+ // Connect and attach epistery
36
+ const epistery = await Epistery.connect();
37
+ await epistery.setDomain('mydomain.com');
38
+ await epistery.attach(app);
39
+
40
+ // Start your server
41
+ const https_server = https.createServer(epistery.config.SNI, app);
42
+ https_server.listen(443);
43
+ ```
44
+
45
+ ## Data Wallets
46
+
47
+ A data wallet is data with chain. The data wallet attaches to the source object with a hash and is used to track
48
+ the provenance, manipulation and usage of the data, per instruction by the owner. The epistery enables IPFS as a
49
+ default storage option for uploaded objects, but there is no requirement to load the data itself on chain, just
50
+ its accounting.
package/SESSION.md ADDED
@@ -0,0 +1,98 @@
1
+ 1# Epistery CLI Session Log
2
+ ## Date: 2025-10-14
3
+
4
+ ### Context
5
+ Building epistery CLI for authenticated requests to wiki.rootz.global. Hours of work building authentication system. Machine has crashed multiple times, losing session context.
6
+
7
+ ### Current State
8
+ - **CLI Location**: `./cli/epistery.mjs`
9
+ - **Identity**: localhost wallet at `~/.epistery/localhost/config.ini`
10
+ - **Address**: `0x8df97495e72461786E263CaECcAf21315E98e9aF`
11
+ - **Default Domain**: Set to `localhost`
12
+ - **Authorization**: This address is authorized on wiki.rootz.global server
13
+ - **Recent Change**: Bot mode now defaults to `true` (was `false`) in cli/epistery.mjs line 127
14
+
15
+ ### Goal
16
+ Post documentation to https://wiki.rootz.global/wiki/Home explaining how other developers can:
17
+ 1. Set up epistery CLI
18
+ 2. Configure authentication
19
+ 3. Post to the wiki from their dev environment using Claude
20
+
21
+ ### Current Issue
22
+ - ✅ Read works: `epistery curl https://wiki.rootz.global/wiki/Home`
23
+ - ❌ Write getting unauthorized (was working before crash)
24
+
25
+ ### Commands That Work
26
+ ```bash
27
+ epistery set-default localhost
28
+ epistery curl https://wiki.rootz.global/wiki/Home
29
+ ```
30
+
31
+ ### Debugging Progress
32
+
33
+ **Bot Mode (Authorization header):**
34
+ - ✅ Signature generation works
35
+ - ❌ Server returns 401 Unauthorized
36
+ - Format: `Authorization: Bot <base64 JSON with address, signature, message>`
37
+
38
+ **Session Mode (Key Exchange):**
39
+ - ✅ Key exchange completes successfully
40
+ - ✅ Server responds: 0x06E2174095fB7cb1251EEf4D229772A59a0C8761
41
+ - ❌ Returns `authenticated: false`
42
+ - ❌ No session cookie set by server
43
+ - This means the server doesn't recognize/trust the client address (9aF)
44
+
45
+ ### Solution Found
46
+
47
+ The bot authentication in Rhonda's account-server (lines 825-909) expects:
48
+ 1. User exists in database ✅
49
+ 2. ACL exists for account access ✅
50
+ 3. Signature verification ✅
51
+
52
+ The user needs to be marked as a system account. Update MongoDB:
53
+ ```
54
+ db.user.updateOne(
55
+ {_id: '0x8df97495e72461786E263CaECcAf21315E98e9aF'},
56
+ {$set: {'options.systemAccount': true}}
57
+ )
58
+ ```
59
+
60
+ ### Next Steps
61
+ 1. Update user record to mark as system account
62
+ 2. Test bot authentication with CLI
63
+ 3. Write and post developer documentation to wiki
64
+
65
+ ### Resolution
66
+
67
+ **Fixed in `/home/msprague/workspace/rootz/rhonda/modules/account-server/index.mjs:866`**
68
+
69
+ Changed from:
70
+ ```javascript
71
+ const userAccount = await this.userCollection.findOne({
72
+ address: address.toLowerCase()
73
+ });
74
+ ```
75
+
76
+ To:
77
+ ```javascript
78
+ const userAccount = await this.userCollection.findOne({
79
+ address: new RegExp(`^${address}$`, 'i')
80
+ });
81
+ ```
82
+
83
+ This makes address lookup case-insensitive, handling addresses stored with mixed case in the database.
84
+
85
+ ### Success
86
+
87
+ ✅ Bot authentication working
88
+ ✅ Test page created at `/wiki/ClaudeTest`
89
+ ✅ Documentation updated on wiki Home page
90
+ ✅ Other developers can now follow the instructions to set up their own agents
91
+
92
+ ### Key Learnings
93
+
94
+ 1. **Case sensitivity**: Ethereum addresses are case-insensitive but often stored with mixed case (checksummed). Always use case-insensitive comparison in MongoDB queries.
95
+
96
+ 2. **Bot authentication flow**: The Epistery CLI signs each request with the wallet's private key. The server verifies the signature, looks up the user by address, checks ACL permissions, and allows/denies the request.
97
+
98
+ 3. **Session persistence**: Creating SESSION.md files for important development sessions helps recover from machine crashes and provides documentation of debugging processes.