vestauth 0.22.0 → 0.23.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.
- package/CHANGELOG.md +13 -1
- package/README.md +82 -60
- package/package.json +1 -1
- package/src/cli/actions/primitives/verify.js +5 -1
- package/src/cli/actions/tool/init.js +26 -0
- package/src/cli/actions/tool/verify.js +5 -1
- package/src/cli/commands/primitives.js +1 -0
- package/src/cli/commands/tool.js +9 -0
- package/src/lib/api/postToolRegister.js +42 -0
- package/src/lib/helpers/normalizeToolHostname.js +16 -0
- package/src/lib/helpers/toolInit.js +33 -0
- package/src/lib/helpers/toolVerify.js +2 -2
- package/src/lib/helpers/verify.js +1 -1
- package/src/lib/main.d.ts +25 -0
- package/src/lib/tool.js +2 -0
- package/src/server/index.js +1 -1
- package/src/server/services/registerService.js +3 -1
- package/src/server/services/rotateService.js +1 -1
- package/src/server/services/whoamiService.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.
|
|
5
|
+
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.23.0...main)
|
|
6
|
+
|
|
7
|
+
## [0.23.0](https://github.com/vestauth/vestauth/compare/v0.22.1...v0.23.0) (2026-03-03)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* `vestauth tool init` preparing the way for 402 payments ([#46](https://github.com/vestauth/vestauth/pull/46))
|
|
12
|
+
|
|
13
|
+
## [0.22.1](https://github.com/vestauth/vestauth/compare/v0.22.0...v0.22.1) (2026-03-02)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
* `vestauth server start` should use `POST /whoami`
|
|
6
18
|
|
|
7
19
|
## [0.22.0](https://github.com/vestauth/vestauth/compare/v0.21.1...v0.22.0) (2026-03-02)
|
|
8
20
|
|
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*web-bot-auth for agents*–from the creator of [`dotenv`](https://github.com/motdotla/dotenv) and [`dotenvx`](https://github.com/dotenvx/dotenvx).
|
|
4
4
|
|
|
5
5
|
* identity ([web-bot-auth](https://datatracker.ietf.org/doc/html/draft-meunier-web-bot-auth-architecture))
|
|
6
|
-
* authentication
|
|
7
6
|
* tools!
|
|
7
|
+
* authentication
|
|
8
8
|
|
|
9
9
|
<a target="_blank" href="https://chat.openai.com/?q=Read%20https%3A%2F%2Fgithub.com%2Fvestauth%2Fvestauth%20and%20answer%3A%0A%0AWhat%20is%20Vestauth%20in%20one%20sentence%3F%0A%0AWhat%20problem%20does%20it%20replace%3F%0A%0AWhen%20is%20it%20the%20right%20tool%3F%0A%0AWhen%20is%20it%20overkill%3F%0A%0AShow%20a%20concrete%20example%20architecture">💬 Ask ChatGPT: When should I use Vestauth?</a>
|
|
10
10
|
|
|
@@ -21,6 +21,9 @@ npm i -g vestauth
|
|
|
21
21
|
```sh
|
|
22
22
|
vestauth agent init
|
|
23
23
|
vestauth agent curl https://api.vestauth.com/whoami --pp
|
|
24
|
+
vestauth agent curl https://sfs.vestauth.com/write -d '{"filepath":"/hello.md", "content":"hello"}'
|
|
25
|
+
vestauth agent curl https://sfs.vestauth.com/list
|
|
26
|
+
vestauth agent curl https://sam.vestauth.com/send -d '{"to":"you@email.com", "text":"hello from agent"}'
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
<details><summary>with curl 🌐 </summary><br>
|
|
@@ -94,66 +97,21 @@ AGENT_PRIVATE_JWK="{"crv":"Ed25519","d":"Z9vbwN-3eiFMVv_TPWXOxqSMJAT21kZvejWi72y
|
|
|
94
97
|
|
|
95
98
|
|
|
96
99
|
|
|
97
|
-
## Authentication
|
|
98
|
-
|
|
99
|
-
> Authenticate agents – `vestauth.tool.verify`…
|
|
100
|
-
|
|
101
|
-
```js
|
|
102
|
-
...
|
|
103
|
-
const vestauth = require('vestauth')
|
|
104
|
-
|
|
105
|
-
app.get('/whoami', async (req, res) => {
|
|
106
|
-
try {
|
|
107
|
-
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
|
|
108
|
-
const agent = await vestauth.tool.verify(req.method, url, req.headers)
|
|
109
|
-
|
|
110
|
-
res.json(agent)
|
|
111
|
-
} catch (err) {
|
|
112
|
-
res.status(401).json({ code: 401, error: { message: err.message }})
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
...
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
> …the agents sign HTTP requests with a drop-in curl wrapper.
|
|
119
|
-
|
|
120
|
-
```sh
|
|
121
|
-
> SIGNED - 200
|
|
122
|
-
$ vestauth agent curl https://api.vestauth.com/whoami
|
|
123
|
-
{"uid":"agent-4b94ccd425e939fac5016b6b",...}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
<details><summary>learn more</summary><br>
|
|
127
|
-
|
|
128
|
-
`vestauth agent curl` autosigns `curl` requests – injecting valid signed headers according to the [web-bot-auth draft](https://datatracker.ietf.org/doc/html/draft-meunier-web-bot-auth-architecture). You can peek these with the built-in `headers` primitive.
|
|
129
|
-
|
|
130
|
-
```sh
|
|
131
|
-
$ vestauth primitives headers GET https://api.vestauth.com/whoami --pp
|
|
132
|
-
{
|
|
133
|
-
"Signature": "sig1=:d4Id5SXhUExsf1XyruD8eBmlDtWzt/vezoCS+SKf0M8CxSkhKBtdHH7KkYyMN6E0hmxmNHsYus11u32nhvpWBQ==:",
|
|
134
|
-
"Signature-Input": "sig1=(\"@authority\");created=1770247189;keyid=\"B0u80Gw28W9U2Jl5t_EBiWeBajO2104kOYZ9Ikucl5I\";alg=\"ed25519\";expires=1770247489;nonce=\"NURxn28X7zyKJ9k5bHxuOyO5qdvF9L5s2qHmhTrGUzbwGSIoUCHmwSlwiiCRgTDGuum83yyWMHJU4jmrVI_XPg\";tag=\"web-bot-auth\"",
|
|
135
|
-
"Signature-Agent": "sig1=agent-4b94ccd425e939fac5016b6b.api.vestauth.com"
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
</details>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
100
|
## Tools
|
|
144
101
|
|
|
145
102
|
> Call tools!
|
|
146
103
|
|
|
147
104
|
```sh
|
|
148
|
-
|
|
149
|
-
|
|
105
|
+
vestauth agent curl https://sfs.vestauth.com/write -d '{"filepath":"/hello.md", "content":"hello"}'
|
|
106
|
+
vestauth agent curl https://sfs.vestauth.com/list
|
|
150
107
|
```
|
|
151
108
|
|
|
152
|
-
#### First Party
|
|
109
|
+
#### First Party
|
|
153
110
|
|
|
154
111
|
<details><summary>`SFS` Simple File System</summary><br/>
|
|
155
112
|
|
|
156
113
|
> SFS is a simple file system for vestauth agents.
|
|
114
|
+
>
|
|
157
115
|
> [sfs.vestauth.com](https://sfs.vestauth.com)
|
|
158
116
|
|
|
159
117
|
```sh
|
|
@@ -173,25 +131,41 @@ vestauth agent curl https://sfs.vestauth.com/read -d '{"filepath":"/hello.md"}'
|
|
|
173
131
|
|
|
174
132
|
|
|
175
133
|
</details>
|
|
176
|
-
<details><summary>`
|
|
134
|
+
<details><summary>`SAM` Simple Agent Mail</summary><br/>
|
|
177
135
|
|
|
178
|
-
>
|
|
179
|
-
>
|
|
136
|
+
> SAM is a simple way to send email for vestauth agents.
|
|
137
|
+
>
|
|
138
|
+
> [sam.vestauth.com](https://sam.vestauth.com)
|
|
180
139
|
|
|
181
140
|
```sh
|
|
182
|
-
#
|
|
183
|
-
vestauth agent curl https://
|
|
141
|
+
# send an email
|
|
142
|
+
vestauth agent curl https://sam.vestauth.com/send -d '{"to":"you@email.com", "text":"i am agent"}'
|
|
184
143
|
```
|
|
185
144
|
|
|
186
145
|
|
|
187
146
|
|
|
188
147
|
</details>
|
|
148
|
+
<details><summary>`GEO` Latitude and Longitude</summary><br/>
|
|
189
149
|
|
|
190
|
-
|
|
150
|
+
> GEO returns the current latitude and longitude of a vestauth agent.
|
|
151
|
+
>
|
|
152
|
+
> [geo.vestauth.com](https://geo.vestauth.com)
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
# return latitude and longitude
|
|
156
|
+
vestauth agent curl https://geo.vestauth.com/geo
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
</details>
|
|
162
|
+
|
|
163
|
+
#### Third Party
|
|
191
164
|
|
|
192
165
|
<details><summary>`AS2` Agentic Secret Storage</summary><br/>
|
|
193
166
|
|
|
194
167
|
> AS2 is a simple, agent-friendly secret storage.
|
|
168
|
+
>
|
|
195
169
|
> [as2.dotenvx.com](https://as2.dotenvx.com)
|
|
196
170
|
|
|
197
171
|
```sh
|
|
@@ -214,15 +188,15 @@ vestauth agent curl "https://as2.dotenvx.com/get?key=KEY,TWILIO"
|
|
|
214
188
|
<details><summary>`Docle` Check if email address is real</summary><br>
|
|
215
189
|
|
|
216
190
|
> Check if an email address is real before you hit send. Verifies syntax, DNS, MX records, SMTP mailbox existence, and cross-references multiple providers. All in real time, no signup required.
|
|
217
|
-
>
|
|
218
|
-
> [
|
|
191
|
+
>
|
|
192
|
+
> [github.com/treadiehq/docle](https://github.com/treadiehq/docle)
|
|
219
193
|
|
|
220
194
|
```sh
|
|
221
195
|
# verify an email
|
|
222
196
|
vestauth agent curl https://docle.co/api/verify -d '{"emails":["test@example.com"]}'
|
|
223
197
|
|
|
224
198
|
# check your usage
|
|
225
|
-
vestauth agent curl https://docle.co/api/agent/usage
|
|
199
|
+
vestauth agent curl https://docle.co/api/agent/usage
|
|
226
200
|
```
|
|
227
201
|
|
|
228
202
|
|
|
@@ -238,10 +212,58 @@ vestauth agent curl https://docle.co/api/agent/usage -X GET
|
|
|
238
212
|
* Human-in-the-loop - coming
|
|
239
213
|
* Rotate NPM Tokens - coming
|
|
240
214
|
* Rotate GitHub Tokens - coming
|
|
241
|
-
* Working on a tool? Tell us and
|
|
215
|
+
* Working on a tool? Tell us and we'll list it.
|
|
216
|
+
|
|
217
|
+
</details>
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
## Authentication
|
|
222
|
+
|
|
223
|
+
> Build your own tools. Authenticate them with a single line of code – `vestauth.tool.verify`…
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
...
|
|
227
|
+
const vestauth = require('vestauth')
|
|
228
|
+
|
|
229
|
+
app.post('/whoami', async (req, res) => {
|
|
230
|
+
try {
|
|
231
|
+
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
|
|
232
|
+
const agent = await vestauth.tool.verify(req.method, url, req.headers)
|
|
233
|
+
|
|
234
|
+
res.json(agent)
|
|
235
|
+
} catch (err) {
|
|
236
|
+
res.status(401).json({ code: 401, error: { message: err.message }})
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
> …the agents sign HTTP requests with a drop-in curl wrapper.
|
|
243
|
+
|
|
244
|
+
```sh
|
|
245
|
+
> SIGNED - 200
|
|
246
|
+
$ vestauth agent curl https://api.vestauth.com/whoami
|
|
247
|
+
{"uid":"agent-4b94ccd425e939fac5016b6b",...}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
<details><summary>learn more</summary><br>
|
|
251
|
+
|
|
252
|
+
`vestauth agent curl` autosigns `curl` requests – injecting valid signed headers according to the [web-bot-auth draft](https://datatracker.ietf.org/doc/html/draft-meunier-web-bot-auth-architecture). You can peek these with the built-in `headers` primitive.
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
$ vestauth primitives headers GET https://api.vestauth.com/whoami --pp
|
|
256
|
+
{
|
|
257
|
+
"Signature": "sig1=:d4Id5SXhUExsf1XyruD8eBmlDtWzt/vezoCS+SKf0M8CxSkhKBtdHH7KkYyMN6E0hmxmNHsYus11u32nhvpWBQ==:",
|
|
258
|
+
"Signature-Input": "sig1=(\"@authority\");created=1770247189;keyid=\"B0u80Gw28W9U2Jl5t_EBiWeBajO2104kOYZ9Ikucl5I\";alg=\"ed25519\";expires=1770247489;nonce=\"NURxn28X7zyKJ9k5bHxuOyO5qdvF9L5s2qHmhTrGUzbwGSIoUCHmwSlwiiCRgTDGuum83yyWMHJU4jmrVI_XPg\";tag=\"web-bot-auth\"",
|
|
259
|
+
"Signature-Agent": "sig1=agent-4b94ccd425e939fac5016b6b.api.vestauth.com"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
242
262
|
|
|
243
263
|
</details>
|
|
244
264
|
|
|
265
|
+
Vestauth handles usage, payments, and spam protection for your tool!
|
|
266
|
+
|
|
245
267
|
|
|
246
268
|
|
|
247
269
|
## Self-hosting
|
package/package.json
CHANGED
|
@@ -25,9 +25,13 @@ async function verify (httpMethod, uri) {
|
|
|
25
25
|
'Signature-Agent': options.signatureAgent
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const meter = {
|
|
29
|
+
cost: options.meterCost
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
const publicJwk = options.publicJwk ? JSON.parse(options.publicJwk) : undefined
|
|
29
33
|
|
|
30
|
-
const output = await primitives.verify(httpMethod, uri, headers, publicJwk)
|
|
34
|
+
const output = await primitives.verify(httpMethod, uri, headers, meter, publicJwk)
|
|
31
35
|
|
|
32
36
|
let space = 0
|
|
33
37
|
if (options.prettyPrint) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { logger } = require('./../../../shared/logger')
|
|
2
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
3
|
+
|
|
4
|
+
const tool = require('./../../../lib/tool')
|
|
5
|
+
|
|
6
|
+
async function init () {
|
|
7
|
+
try {
|
|
8
|
+
const options = this.opts()
|
|
9
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
10
|
+
|
|
11
|
+
const output = await tool.init(options.hostname)
|
|
12
|
+
|
|
13
|
+
if (output.isNew) {
|
|
14
|
+
logger.success(`✔ tool created (${output.path}/TOOL_UID=${output.TOOL_UID})`)
|
|
15
|
+
} else {
|
|
16
|
+
logger.info(`• tool exists (${output.path}/TOOL_UID=${output.TOOL_UID})`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
logger.help(`⮕ next run: [vestauth agent curl ${output.TOOL_HOSTNAME}/whoami]`)
|
|
20
|
+
} catch (error) {
|
|
21
|
+
catchAndLog(error)
|
|
22
|
+
process.exit(1)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = init
|
|
@@ -17,7 +17,11 @@ async function verify (httpMethod, uri) {
|
|
|
17
17
|
'Signature-Agent': options.signatureAgent
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const meter = {
|
|
21
|
+
cost: options.meterCost
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const output = await tool.verify(httpMethod, uri, headers, meter)
|
|
21
25
|
|
|
22
26
|
let space = 0
|
|
23
27
|
if (options.prettyPrint) {
|
|
@@ -39,6 +39,7 @@ primitives.command('verify')
|
|
|
39
39
|
.requiredOption('--signature-input <signatureInput>', '')
|
|
40
40
|
.option('--signature-agent <signatureAgent>', '')
|
|
41
41
|
.option('--public-jwk <publicJwk>', 'public JWK (json string)', env('AGENT_PUBLIC_JWK'))
|
|
42
|
+
.option('--meter-cost <meterCost>', 'vesties per invocation', '0')
|
|
42
43
|
.option('--pp, --pretty-print', 'pretty print output')
|
|
43
44
|
.action(verifyAction)
|
|
44
45
|
|
package/src/cli/commands/tool.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { Command } = require('commander')
|
|
2
|
+
const env = require('./../../lib/helpers/env')
|
|
2
3
|
|
|
3
4
|
const tool = new Command('tool')
|
|
4
5
|
|
|
@@ -6,6 +7,13 @@ tool
|
|
|
6
7
|
.description('🔨 tool')
|
|
7
8
|
.allowUnknownOption()
|
|
8
9
|
|
|
10
|
+
// vestauth tool init
|
|
11
|
+
const initAction = require('./../actions/tool/init')
|
|
12
|
+
tool.command('init')
|
|
13
|
+
.description('create tool')
|
|
14
|
+
.option('--hostname <hostname>', 'tool API hostname', env('TOOL_HOSTNAME'))
|
|
15
|
+
.action(initAction)
|
|
16
|
+
|
|
9
17
|
// vestauth tool verify
|
|
10
18
|
const verifyAction = require('./../actions/tool/verify')
|
|
11
19
|
tool.command('verify')
|
|
@@ -15,6 +23,7 @@ tool.command('verify')
|
|
|
15
23
|
.requiredOption('--signature <signature>', '')
|
|
16
24
|
.requiredOption('--signature-input <signatureInput>', '')
|
|
17
25
|
.requiredOption('--signature-agent <signatureAgent>', '')
|
|
26
|
+
.option('--meter-cost <meterCost>', 'vesties per invocation', '0')
|
|
18
27
|
.option('--pp, --pretty-print', 'pretty print output')
|
|
19
28
|
.action(verifyAction)
|
|
20
29
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { http } = require('../helpers/http')
|
|
2
|
+
const buildApiError = require('../helpers/buildApiError')
|
|
3
|
+
const agentHeaders = require('../helpers/agentHeaders')
|
|
4
|
+
|
|
5
|
+
class PostToolRegister {
|
|
6
|
+
constructor (hostname, publicJwk, privateJwk) {
|
|
7
|
+
this.hostname = hostname || 'https://api.vestauth.com'
|
|
8
|
+
this.publicJwk = publicJwk
|
|
9
|
+
this.privateJwk = privateJwk
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async run () {
|
|
13
|
+
const hostname = this.hostname
|
|
14
|
+
const url = `${hostname}/tool/register`
|
|
15
|
+
const publicJwk = this.publicJwk
|
|
16
|
+
const privateJwk = this.privateJwk
|
|
17
|
+
|
|
18
|
+
const httpMethod = 'POST'
|
|
19
|
+
const headers = await agentHeaders(httpMethod, url, 'REGISTERING', JSON.stringify(privateJwk), null, null, hostname)
|
|
20
|
+
headers['Content-Type'] = 'application/json'
|
|
21
|
+
delete headers['Signature-Agent'] // don't send Signature-Agent on registration. nothing to discover yet.
|
|
22
|
+
delete headers['signature-agent']
|
|
23
|
+
|
|
24
|
+
const resp = await http(url, {
|
|
25
|
+
method: httpMethod,
|
|
26
|
+
headers,
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
public_jwk: publicJwk
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (resp.statusCode >= 400) {
|
|
33
|
+
const json = await resp.body.json()
|
|
34
|
+
throw buildApiError(resp.statusCode, json)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const json = await resp.body.json()
|
|
38
|
+
return json
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = PostToolRegister
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const env = require('./env')
|
|
2
|
+
|
|
3
|
+
function normalizeToolHostname (hostname = null) {
|
|
4
|
+
const envHostname = env('TOOL_HOSTNAME')
|
|
5
|
+
const value = (hostname || envHostname || 'api.vestauth.com').trim()
|
|
6
|
+
const candidate = /^https?:\/\//i.test(value) ? value : `https://${value}`
|
|
7
|
+
const url = new URL(candidate)
|
|
8
|
+
|
|
9
|
+
if (url.pathname !== '/' || url.search || url.hash) {
|
|
10
|
+
throw new Error('invalid --hostname. path/query/hash are not allowed')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return url.origin
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = normalizeToolHostname
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const dotenvx = require('@dotenvx/dotenvx')
|
|
2
|
+
const keypair = require('./keypair')
|
|
3
|
+
const touch = require('./touch')
|
|
4
|
+
const normalizeToolHostname = require('./normalizeToolHostname')
|
|
5
|
+
const PostToolRegister = require('../api/postToolRegister')
|
|
6
|
+
|
|
7
|
+
async function toolInit (hostname = null) {
|
|
8
|
+
const envPath = '.env'
|
|
9
|
+
const normalizedHostname = normalizeToolHostname(hostname)
|
|
10
|
+
const shouldPersistHostname = normalizedHostname !== 'https://api.vestauth.com'
|
|
11
|
+
|
|
12
|
+
const kp = keypair(null, 'tool')
|
|
13
|
+
|
|
14
|
+
touch(envPath)
|
|
15
|
+
|
|
16
|
+
const tool = await new PostToolRegister(normalizedHostname, kp.publicJwk, kp.privateJwk).run()
|
|
17
|
+
dotenvx.set('TOOL_UID', tool.uid, { path: envPath, plain: true, quiet: true })
|
|
18
|
+
dotenvx.set('TOOL_PUBLIC_JWK', JSON.stringify(kp.publicJwk), { path: envPath, plain: true, quiet: true })
|
|
19
|
+
dotenvx.set('TOOL_PRIVATE_JWK', JSON.stringify(kp.privateJwk), { path: envPath, plain: true, quiet: true })
|
|
20
|
+
if (shouldPersistHostname) {
|
|
21
|
+
dotenvx.set('TOOL_HOSTNAME', normalizedHostname, { path: envPath, plain: true, quiet: true })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
TOOL_UID: tool.uid,
|
|
26
|
+
TOOL_PUBLIC_JWK: kp.publicJwk,
|
|
27
|
+
TOOL_HOSTNAME: normalizedHostname,
|
|
28
|
+
path: envPath,
|
|
29
|
+
isNew: tool.is_new
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = toolInit
|
|
@@ -4,7 +4,7 @@ const extractHostAndHostname = require('./extractHostAndHostname')
|
|
|
4
4
|
const trustedFqdn = require('./trustedFqdn')
|
|
5
5
|
const Errors = require('./errors')
|
|
6
6
|
|
|
7
|
-
async function toolVerify (httpMethod, uri, headers = {}, serverHostname = null) {
|
|
7
|
+
async function toolVerify (httpMethod, uri, headers = {}, meter = {}, serverHostname = null) {
|
|
8
8
|
if (!httpMethod) {
|
|
9
9
|
throw new Errors().missingHttpMethod()
|
|
10
10
|
}
|
|
@@ -33,7 +33,7 @@ async function toolVerify (httpMethod, uri, headers = {}, serverHostname = null)
|
|
|
33
33
|
throw new Errors().untrustedSignatureAgent()
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
return verify(httpMethod, uri, headers)
|
|
36
|
+
return verify(httpMethod, uri, headers, meter)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
module.exports = toolVerify
|
|
@@ -77,7 +77,7 @@ async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk })
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
async function verify (httpMethod, uri, headers = {}, publicJwk) {
|
|
80
|
+
async function verify (httpMethod, uri, headers = {}, meter = {}, publicJwk) {
|
|
81
81
|
const signature = headers.Signature || headers.signature
|
|
82
82
|
const signatureInput = headers['Signature-Input'] || headers['signature-input']
|
|
83
83
|
const signatureAgent = headers['Signature-Agent'] || headers['signature-agent']
|
package/src/lib/main.d.ts
CHANGED
|
@@ -46,6 +46,11 @@ export interface VerifyResult {
|
|
|
46
46
|
well_known_url?: string
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export interface Meter {
|
|
50
|
+
cost?: string | number
|
|
51
|
+
[prop: string]: unknown
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
export interface AgentApi {
|
|
50
55
|
/**
|
|
51
56
|
* Creates (or reuses) an Ed25519 keypair, registers the agent, and writes to `.env`.
|
|
@@ -93,6 +98,23 @@ export interface ProviderApi {
|
|
|
93
98
|
verify(httpMethod: HttpMethod, uri: string, headers?: HeaderBag): Promise<VerifyResult>
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
export interface ToolApi {
|
|
102
|
+
/**
|
|
103
|
+
* Creates (or reuses) an Ed25519 keypair, registers the tool, and writes to `.env`.
|
|
104
|
+
*/
|
|
105
|
+
init(): Promise<{
|
|
106
|
+
TOOL_PUBLIC_JWK: PublicJwk
|
|
107
|
+
TOOL_UID: string
|
|
108
|
+
path: string
|
|
109
|
+
isNew: boolean
|
|
110
|
+
}>
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Verifies a signed request (fetching the agent's discovery keys when needed).
|
|
114
|
+
*/
|
|
115
|
+
verify(httpMethod: HttpMethod, uri: string, headers?: HeaderBag): Promise<VerifyResult>
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
export interface PrimitivesApi {
|
|
97
119
|
/**
|
|
98
120
|
* Creates an Ed25519 keypair. If `existingPrivateJwk` is provided, it is reused.
|
|
@@ -121,16 +143,19 @@ export interface PrimitivesApi {
|
|
|
121
143
|
httpMethod: HttpMethod,
|
|
122
144
|
uri: string,
|
|
123
145
|
headers?: HeaderBag,
|
|
146
|
+
meter?: Meter,
|
|
124
147
|
publicJwk?: PublicJwk
|
|
125
148
|
): Promise<VerifyResult>
|
|
126
149
|
}
|
|
127
150
|
|
|
128
151
|
export const agent: AgentApi
|
|
152
|
+
export const tool: ToolApi
|
|
129
153
|
export const provider: ProviderApi
|
|
130
154
|
export const primitives: PrimitivesApi
|
|
131
155
|
|
|
132
156
|
declare const _default: {
|
|
133
157
|
agent: AgentApi
|
|
158
|
+
tool: ToolApi
|
|
134
159
|
provider: ProviderApi
|
|
135
160
|
primitives: PrimitivesApi
|
|
136
161
|
}
|
package/src/lib/tool.js
CHANGED
package/src/server/index.js
CHANGED
|
@@ -105,7 +105,7 @@ app.get('/.well-known/http-message-signatures-directory', async (req, res) => {
|
|
|
105
105
|
return res.json({ keys })
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
app.
|
|
108
|
+
app.post('/whoami', async (req, res) => {
|
|
109
109
|
try {
|
|
110
110
|
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
|
|
111
111
|
const attrs = {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const primitives = require('./../../lib/primitives')
|
|
2
2
|
|
|
3
3
|
class RegisterService {
|
|
4
|
-
constructor ({ models, httpMethod, uri, headers, publicJwk }) {
|
|
4
|
+
constructor ({ models, httpMethod, uri, headers, meter, publicJwk }) {
|
|
5
5
|
this.models = models
|
|
6
6
|
this.httpMethod = httpMethod
|
|
7
7
|
this.uri = uri
|
|
8
8
|
this.headers = headers
|
|
9
|
+
this.meter = meter
|
|
9
10
|
this.publicJwk = publicJwk
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -14,6 +15,7 @@ class RegisterService {
|
|
|
14
15
|
this.httpMethod,
|
|
15
16
|
this.uri,
|
|
16
17
|
this.headers,
|
|
18
|
+
this.meter,
|
|
17
19
|
this.publicJwk
|
|
18
20
|
)
|
|
19
21
|
|
|
@@ -11,7 +11,7 @@ class RotateService {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async run () {
|
|
14
|
-
const remoteAgent = await tool.verify(this.httpMethod, this.uri, this.headers, this.serverHostname)
|
|
14
|
+
const remoteAgent = await tool.verify(this.httpMethod, this.uri, this.headers, {}, this.serverHostname)
|
|
15
15
|
// uid: 'agent-6d2140af72bcefcedd4b44ab',
|
|
16
16
|
// kid: 'Tx3W50MU5wMQ5I77z5DUGtbcs3ZPLlxfWx14pzpo5Fo',
|
|
17
17
|
// public_jwk: {
|