vestauth 0.14.0 → 0.15.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 CHANGED
@@ -2,7 +2,20 @@
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.14.0...main)
5
+ [Unreleased](https://github.com/vestauth/vestauth/compare/v0.15.0...main)
6
+
7
+ ## [0.15.0](https://github.com/vestauth/vestauth/compare/v0.14.1...v0.15.0) (2026-02-20)
8
+
9
+ ### Added
10
+
11
+ * Add support for http localhost ([#23](https://github.com/vestauth/vestauth/pull/23))
12
+ * Add support for localhost public key discovery ([#24](https://github.com/vestauth/vestauth/pull/24))
13
+
14
+ ## [0.14.1](https://github.com/vestauth/vestauth/compare/v0.14.0...v0.14.1) (2026-02-18)
15
+
16
+ ### Changed
17
+
18
+ * Rerelease after npm token failed
6
19
 
7
20
  ## [0.14.0](https://github.com/vestauth/vestauth/compare/v0.13.0...v0.14.0) (2026-02-18)
8
21
 
package/README.md CHANGED
@@ -212,6 +212,7 @@ $ vestauth agent init
212
212
  <details><summary>`agent init --hostname`</summary><br>
213
213
 
214
214
  Use `--hostname` to override the agent API hostname (defaults to `AGENT_HOSTNAME`, then `api.vestauth.com`):
215
+ When no scheme is provided, `https://` is assumed. For local non-TLS endpoints, pass `http://...` explicitly.
215
216
 
216
217
  ```sh
217
218
  $ vestauth agent init --hostname https://vestauth.yoursite.com
@@ -372,7 +373,16 @@ Use vestauth directly in code.
372
373
  Verify and authenticate an agent's cryptographic identity.
373
374
 
374
375
  ```js
375
- const agent = await vestauth.tool.verify(req.method, url, req.headers)
376
+ const agent = await vestauth.tool.verify(httpMethod, url, headers)
377
+ ```
378
+
379
+ </details>
380
+ <details><summary>`primitives.verify()`</summary><br>
381
+
382
+ Verify and authenticate a signed http request.
383
+
384
+ ```js
385
+ await vestauth.primitives.verify(httpMethod, url, headers, publicJwk)
376
386
  ```
377
387
 
378
388
  </details>
@@ -381,7 +391,7 @@ const agent = await vestauth.tool.verify(req.method, url, req.headers)
381
391
 
382
392
  ## Available Tools
383
393
 
384
- > Vestauth is pioneering the auth layer for agents. Get in early on this distribution train. [Become a vestauth tool](mailto:mot@dotenvx.com)
394
+ > List of tools. We're actively building tools and looking for others to help grow the vestauth ecosystem with calls for tools like sending email, sms, uploading files and more. Vestauth agents need more tools. A tool is just a sharp API call. Add your tool here.
385
395
 
386
396
  * AS2 (Agentic Secret Storage) - https://as2.dotenvx.com
387
397
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vestauth",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "auth for agents–from the creator of dotenvx",
5
5
  "keywords": [
6
6
  "vestauth",
@@ -16,7 +16,7 @@ async function init () {
16
16
  logger.info(`• agent exists (${output.path}/AGENT_UID=${output.AGENT_UID})`)
17
17
  }
18
18
 
19
- logger.help('⮕ next run: [vestauth agent curl https://api.vestauth.com/whoami]')
19
+ logger.help(`⮕ next run: [vestauth agent curl ${output.AGENT_HOSTNAME}/whoami]`)
20
20
  } catch (error) {
21
21
  catchAndLog(error)
22
22
  process.exit(1)
@@ -10,12 +10,13 @@ class PostRegister {
10
10
  }
11
11
 
12
12
  async run () {
13
- const url = `${this.hostname}/register`
13
+ const hostname = this.hostname
14
+ const url = `${hostname}/register`
14
15
  const publicJwk = this.publicJwk
15
16
  const privateJwk = this.privateJwk
16
17
 
17
18
  const httpMethod = 'POST'
18
- const headers = await agentHeaders(httpMethod, url, 'REGISTERING', JSON.stringify(privateJwk))
19
+ const headers = await agentHeaders(httpMethod, url, 'REGISTERING', JSON.stringify(privateJwk), null, null, hostname)
19
20
  headers['Content-Type'] = 'application/json'
20
21
 
21
22
  const resp = await http(url, {
@@ -1,7 +1,7 @@
1
1
  const headers = require('./headers')
2
2
  const identity = require('./identity')
3
3
 
4
- async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag = 'web-bot-auth', nonce = null) {
4
+ async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag = 'web-bot-auth', nonce = null, hostname = null) {
5
5
  if (!privateJwk) {
6
6
  privateJwk = identity().privateJwk
7
7
  }
@@ -10,7 +10,7 @@ async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag
10
10
  uid = identity().uid
11
11
  }
12
12
 
13
- return await headers(httpMethod, uri, uid, privateJwk, tag, nonce)
13
+ return await headers(httpMethod, uri, uid, privateJwk, tag, nonce, hostname)
14
14
  }
15
15
 
16
16
  module.exports = agentHeaders
@@ -22,12 +22,13 @@ async function agentInit (hostname = null) {
22
22
  dotenvx.set('AGENT_PUBLIC_JWK', JSON.stringify(kp.publicJwk), { path: envPath, plain: true, quiet: true })
23
23
  dotenvx.set('AGENT_PRIVATE_JWK', JSON.stringify(kp.privateJwk), { path: envPath, plain: true, quiet: true })
24
24
  if (shouldPersistHostname) {
25
- dotenvx.set('AGENT_HOSTNAME', new URL(normalizedHostname).host, { path: envPath, plain: true, quiet: true })
25
+ dotenvx.set('AGENT_HOSTNAME', normalizedHostname, { path: envPath, plain: true, quiet: true })
26
26
  }
27
27
 
28
28
  return {
29
- AGENT_PUBLIC_JWK: kp.publicJwk,
30
29
  AGENT_UID: agent.uid,
30
+ AGENT_PUBLIC_JWK: kp.publicJwk,
31
+ AGENT_HOSTNAME: normalizedHostname,
31
32
  path: envPath,
32
33
  isNew: agent.is_new
33
34
  }
@@ -4,12 +4,16 @@ const signatureParams = require('./signatureParams')
4
4
  const webBotAuthSignature = require('./webBotAuthSignature')
5
5
  const env = require('./env')
6
6
 
7
- function getAgentDiscoveryDomain () {
8
- const hostname = (env('AGENT_HOSTNAME') || process.env.AGENT_HOSTNAME || 'api.vestauth.com').trim().toLowerCase()
9
- return hostname.replace(/^https?:\/\//, '').split('/')[0]
7
+ function getAgentDiscoveryOrigin (hostname = null) {
8
+ if (!hostname) {
9
+ hostname = (env('AGENT_HOSTNAME') || process.env.AGENT_HOSTNAME || 'api.vestauth.com').trim()
10
+ }
11
+
12
+ const candidate = /^https?:\/\//i.test(hostname) ? hostname : `https://${hostname}`
13
+ return new URL(candidate).origin
10
14
  }
11
15
 
12
- async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth', nonce = null) {
16
+ async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth', nonce = null, hostname = null) {
13
17
  if (!uid) throw new Errors().missingUid()
14
18
  if (!privateJwk) throw new Errors().missingPrivateJwk()
15
19
 
@@ -27,12 +31,14 @@ async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth',
27
31
 
28
32
  const signatureInput = signatureParams(privateJwk.kid, tag, nonce)
29
33
  const signature = webBotAuthSignature(httpMethod, uri, signatureInput, privateJwk)
30
- const signatureAgent = `${uid}.${getAgentDiscoveryDomain()}` // no scheme; fqdn only
34
+ const discoveryOrigin = getAgentDiscoveryOrigin(hostname)
35
+ const discoveryUrl = new URL(discoveryOrigin)
36
+ const signatureAgent = `${discoveryUrl.protocol}//${uid}.${discoveryUrl.host}`
31
37
 
32
38
  return {
33
39
  Signature: `sig1=:${signature}:`,
34
40
  'Signature-Input': `sig1=${signatureInput}`,
35
- 'Signature-Agent': `sig1=${signatureAgent}`
41
+ 'Signature-Agent': `sig1="${signatureAgent}"`
36
42
  }
37
43
  }
38
44
 
@@ -0,0 +1,6 @@
1
+ function isLocalhost (wellKnownUrl) {
2
+ const url = new URL(wellKnownUrl)
3
+ return url.hostname === 'localhost' || url.hostname.endsWith('.localhost')
4
+ }
5
+
6
+ module.exports = isLocalhost
@@ -9,6 +9,7 @@ const authorityMessage = require('./authorityMessage')
9
9
  const publicJwkObject = require('./publicJwkObject')
10
10
  const verifyAgentFqdn = require('./verifyAgentFqdn')
11
11
  const Errors = require('./errors')
12
+ const isLocalhost = require('./isLocalhost')
12
13
 
13
14
  async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk }) {
14
15
  let uid
@@ -19,10 +20,13 @@ async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk })
19
20
 
20
21
  if (signatureAgent) {
21
22
  const { value } = parseSignatureAgentHeader(signatureAgent)
22
- const fqdn = value
23
- verifyAgentFqdn(fqdn)
24
- const origin = `https://${fqdn}`
25
- uid = fqdn.split('.')[0]
23
+ const isUri = /^https?:\/\//i.test(value)
24
+ const origin = isUri ? new URL(value).origin : `https://${value}`
25
+ const hostname = new URL(origin).hostname
26
+ if (!isLocalhost(origin)) {
27
+ verifyAgentFqdn(hostname)
28
+ }
29
+ uid = hostname.split('.')[0]
26
30
  wellKnownUrl = `${origin}/.well-known/http-message-signatures-directory`
27
31
  }
28
32
 
@@ -33,7 +37,22 @@ async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk })
33
37
  return { publicJwk: null }
34
38
  }
35
39
 
36
- const resp = await http(wellKnownUrl, { method: 'GET', headers: { 'Content-Type': 'application/json' } })
40
+ let requestUrl = wellKnownUrl
41
+ const requestHeaders = {}
42
+
43
+ const url = new URL(requestUrl)
44
+ if (isLocalhost(wellKnownUrl)) {
45
+ const port = url.port || '80'
46
+ requestUrl = `http://127.0.0.1:${port}${url.pathname}`
47
+ requestHeaders.host = url.host
48
+ }
49
+
50
+ const opts = { method: 'GET' }
51
+ if (Object.keys(requestHeaders).length > 0) {
52
+ opts.headers = requestHeaders
53
+ }
54
+
55
+ const resp = await http(requestUrl, opts)
37
56
  if (resp.statusCode >= 400) {
38
57
  const json = await resp.body.json()
39
58
  throw buildApiError(resp.statusCode, json)
@@ -16,7 +16,6 @@ function verifyAgentFqdn (fqdn) {
16
16
  if (!fqdn || typeof fqdn !== 'string') {
17
17
  throw new Errors().invalidSignatureAgent()
18
18
  }
19
-
20
19
  const pattern = getToolFqdnRegex()
21
20
  if (!pattern.test(fqdn)) {
22
21
  throw new Errors().invalidSignatureAgent()