Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fa4bc57
feat: add republish record function
2color Feb 24, 2025
4cdd212
fix: linting errors
2color Feb 24, 2025
26d835a
feat: add republishing logic
2color Mar 7, 2025
a4cd0ac
Merge remote-tracking branch 'origin/main' into add-republishing
2color Mar 13, 2025
1e20e38
Merge branch 'main' into add-republishing
2color Mar 20, 2025
458f50b
wip
2color Apr 29, 2025
42e6db2
Merge remote-tracking branch 'origin/main' into add-republishing
2color Jun 17, 2025
251d5fd
feat: implement republishing with keychain
2color Jun 18, 2025
62214df
feat: use protons to encode ipn metadata for store
2color Jun 19, 2025
99fa240
feat: move libp2p to the helia class
2color Jun 20, 2025
066096b
fix: add type assertion
2color Jun 20, 2025
2ef32b5
fix: import
2color Jun 20, 2025
374c3d6
fix: more type error fixes
2color Jun 20, 2025
4b73c57
fix: helia instance type error
2color Jun 23, 2025
9f5ccdf
fix: make publish metadata optional
2color Jun 23, 2025
b8eb0af
chore: rename to publish metadata for clarity
2color Jun 23, 2025
3349164
test: stub libp2p for ipns
2color Jun 23, 2025
f1ff57f
fix: typo in function
2color Jun 23, 2025
6657c39
test: update tests to reflect new api
2color Jun 23, 2025
f0d752c
chore: fix tests and make libp2p required
2color Jun 24, 2025
b1c536a
chore: run linter
2color Jun 25, 2025
afabd83
test: add republish tests
2color Jun 25, 2025
0d7b5ea
fix: types
2color Jun 25, 2025
216c72b
test: fix types in interop tests
2color Jun 25, 2025
0135d3d
test: fix interop tests to use new ipns api
2color Jun 25, 2025
19ac9b6
chore: fix lint errors
2color Jun 25, 2025
0e3fddc
fix: delete metadata after deleting a record
2color Jun 25, 2025
b8464eb
fix: use logger in ipns local store
2color Jun 26, 2025
f5272c0
docs: use updated api in docs
2color Jun 26, 2025
34c5b3c
chore: fix linting errors
2color Jun 26, 2025
3ce2b3c
fix: reutnr default libp2p services with no conf
2color Jun 26, 2025
3415283
Merge remote-tracking branch 'origin/main' into add-republishing
2color Jul 9, 2025
a22a3d0
chore: move optional metadata to options
2color Jul 10, 2025
07ad78f
chore: fix lint error
2color Jul 10, 2025
0d3098f
fix: types in example
2color Jul 10, 2025
db7421c
test: fix repoublish tests
2color Jul 10, 2025
93e42f7
chore: fix deps
2color Jul 10, 2025
accbc83
chore: add fleek to dictionary
2color Jul 10, 2025
c216b5c
test: remove fake timers and use short intervals
2color Jul 10, 2025
7c4b64c
test: remove unneeded tests
2color Jul 10, 2025
33df033
test: simplify and tame flakiness hopefully
2color Jul 10, 2025
ab17922
test: remove only
2color Jul 10, 2025
a17581f
test: wait longer in tests for slow event loops 😅
2color Jul 10, 2025
f3e8262
test: stabilise tests
2color Jul 11, 2025
c8b561f
feat: publish records concurrently
2color Jul 11, 2025
f33a66d
test: make tests more robust
2color Jul 11, 2025
e8c33c6
test: make sure we clean up after tests properly
2color Jul 11, 2025
36c1235
chore: add libp2p utils as dep
2color Jul 11, 2025
3f84f75
docs: document republish behaviour
2color Jul 23, 2025
3441739
fix: handle marshaling errors seprately
2color Jul 23, 2025
de631f5
Merge branch 'main' into add-republishing
2color Jul 23, 2025
536ec78
feat: add an event emitter to helia for consumers
2color Jul 23, 2025
7fc3971
fix: ensure cleanup when helia is stopped
2color Jul 23, 2025
c1b784f
Apply suggestions from code review
2color Jul 23, 2025
a15d3b5
fix: throw when ipns methods called after stopped
2color Jul 23, 2025
f81cf73
test: bring back dnslink tests
2color Jul 23, 2025
b70d52e
fix!: accept keyName for unpublishing records
2color Jul 23, 2025
6c25938
fix!: remove offline option for republish record
2color Jul 23, 2025
af7569d
chore: remove comments about storing record
2color Jul 23, 2025
27ed15e
test: fix progress event test
2color Jul 24, 2025
1da8b63
test: publish handling of local store errors
2color Jul 24, 2025
47c118b
fix: emit error during republish
2color Jul 24, 2025
72c079c
test: test local store errors during repbulish
2color Jul 24, 2025
ea58972
fix: typo
2color Jul 25, 2025
25082d1
fix: republish records within expiry threshold
2color Aug 22, 2025
abf9161
chore: typo
2color Aug 22, 2025
7035d49
fix: test
2color Aug 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/dictionary.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dont
fleek
31 changes: 30 additions & 1 deletion packages/interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import type { Blocks } from './blocks.js'
import type { Pins } from './pins.js'
import type { Routing } from './routing.js'
import type { AbortOptions, ComponentLogger, Libp2p, Metrics } from '@libp2p/interface'
import type { AbortOptions, ComponentLogger, Libp2p, Metrics, TypedEventEmitter } from '@libp2p/interface'
import type { DNS } from '@multiformats/dns'
import type { Datastore } from 'interface-datastore'
import type { Await } from 'interface-store'
Expand Down Expand Up @@ -54,6 +54,11 @@ export interface Helia<T extends Libp2p = Libp2p> {
*/
datastore: Datastore

/**
* Event emitter for Helia start and stop events
*/
events: TypedEventEmitter<HeliaEvents<T>>

/**
* Pinning operations for blocks in the blockstore
*/
Expand Down Expand Up @@ -119,6 +124,30 @@ export interface GCOptions extends AbortOptions, ProgressOptions<GcEvents> {

}

export interface HeliaEvents<T extends Libp2p = Libp2p> {
/**
* This event notifies listeners that the node has started
*
* ```TypeScript
* helia.addEventListener('start', (event) => {
* console.info(event.detail.libp2p.isStarted()) // true
* })
* ```
*/
start: CustomEvent<Helia<T>>

/**
* This event notifies listeners that the node has stopped
*
* ```TypeScript
* helia.addEventListener('stop', (event) => {
* console.info(event.detail.libp2p.isStarted()) // false
* })
* ```
*/
stop: CustomEvent<Helia<T>>
}

export * from './blocks.js'
export * from './errors.js'
export * from './pins.js'
Expand Down
4 changes: 3 additions & 1 deletion packages/interop/src/ipns-pubsub.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ keyTypes.filter(keyType => keyType !== 'RSA').forEach(keyType => {
const cid = CID.createV1(raw.code, digest)

const privateKey = await generateKeyPair('Ed25519')
const keyName = 'my-ipns-key'
await helia.libp2p.services.keychain.importKey(keyName, privateKey)

// first call to pubsub resolver will fail but we should trigger
// subscribing pubsub for updates
Expand Down Expand Up @@ -103,7 +105,7 @@ keyTypes.filter(keyType => keyType !== 'RSA').forEach(keyType => {
})

// publish should now succeed
await name.publish(privateKey, cid)
await name.publish(keyName, cid)

// kubo should now be able to resolve IPNS name instantly
const resolved = await last(kubo.api.name.resolve(privateKey.publicKey.toString(), {
Expand Down
5 changes: 3 additions & 2 deletions packages/interop/src/ipns.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ keyTypes.forEach(type => {
await createNodes('kubo')

const privateKey = await generateKeyPair('Ed25519')

await name.publish(privateKey, value)
const keyName = 'my-ipns-key'
await helia.libp2p.services.keychain.importKey(keyName, privateKey)
await name.publish(keyName, value)

const resolved = await last(kubo.api.name.resolve(privateKey.publicKey.toString()))

Expand Down
68 changes: 26 additions & 42 deletions packages/ipns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,19 @@ With IPNSRouting routers:
import { createHelia } from 'helia'
import { ipns } from '@helia/ipns'
import { unixfs } from '@helia/unixfs'
import { generateKeyPair } from '@libp2p/crypto/keys'

const helia = await createHelia()
const name = ipns(helia)

// create a keypair to publish an IPNS name
const privateKey = await generateKeyPair('Ed25519')

// store some data to publish
const fs = unixfs(helia)
const cid = await fs.addBytes(Uint8Array.from([0, 1, 2, 3, 4]))

// publish the name
await name.publish(privateKey, cid)
const { publicKey } = await name.publish('key-1', cid)

// resolve the name
const result = await name.resolve(privateKey.publicKey)
const result = await name.resolve(publicKey)

console.info(result.cid, result.path)
```
Expand All @@ -75,24 +71,18 @@ import { generateKeyPair } from '@libp2p/crypto/keys'
const helia = await createHelia()
const name = ipns(helia)

// create a keypair to publish an IPNS name
const privateKey = await generateKeyPair('Ed25519')

// store some data to publish
const fs = unixfs(helia)
const cid = await fs.addBytes(Uint8Array.from([0, 1, 2, 3, 4]))

// publish the name
await name.publish(privateKey, cid)

// create another keypair to re-publish the original record
const recursivePrivateKey = await generateKeyPair('Ed25519')
const { publicKey } = await name.publish('key-1', cid)

// publish the recursive name
await name.publish(recursivePrivateKey, privateKey.publicKey)
const { publicKey: recursivePublicKey } = await name.publish('key-2', publicKey)

// resolve the name recursively - it resolves until a CID is found
const result = await name.resolve(recursivePrivateKey.publicKey)
const result = await name.resolve(recursivePublicKey)
console.info(result.cid.toString() === cid.toString()) // true
```

Expand All @@ -109,9 +99,6 @@ import { generateKeyPair } from '@libp2p/crypto/keys'
const helia = await createHelia()
const name = ipns(helia)

// create a keypair to publish an IPNS name
const privateKey = await generateKeyPair('Ed25519')

// store some data to publish
const fs = unixfs(helia)
const fileCid = await fs.addBytes(Uint8Array.from([0, 1, 2, 3, 4]))
Expand All @@ -121,10 +108,10 @@ const dirCid = await fs.addDirectory()
const finalDirCid = await fs.cp(fileCid, dirCid, '/foo.txt')

// publish the name
await name.publish(privateKey, `/ipfs/${finalDirCid}/foo.txt`)
const { publicKey } = await name.publish('key-1', `/ipfs/${finalDirCid}/foo.txt`)

// resolve the name
const result = await name.resolve(privateKey.publicKey)
const result = await name.resolve(publicKey)

console.info(result.cid, result.path) // QmFoo.. 'foo.txt'
```
Expand Down Expand Up @@ -167,18 +154,16 @@ const name = ipns(helia, {
]
})

// create a keypair to publish an IPNS name
const privateKey = await generateKeyPair('Ed25519')

// store some data to publish
const fs = unixfs(helia)
const cid = await fs.addBytes(Uint8Array.from([0, 1, 2, 3, 4]))

// publish the name
await name.publish(privateKey, cid)
const { publicKey } = await name.publish('key-1', cid)

// resolve the name
const result = await name.resolve(privateKey.publicKey)
const result = await name.resolve(publicKey)
```

## Example - Using custom DNS over HTTPS resolvers
Expand All @@ -190,20 +175,17 @@ import { createHelia } from 'helia'
import { ipns } from '@helia/ipns'
import { dns } from '@multiformats/dns'
import { dnsOverHttps } from '@multiformats/dns/resolvers'
import { helia } from '@helia/ipns/routing'
import type { DefaultLibp2pServices } from 'helia'
import type { Libp2p } from '@libp2p/interface'

const node = await createHelia({
const node = await createHelia<Libp2p<DefaultLibp2pServices>>({
dns: dns({
resolvers: {
'.': dnsOverHttps('https://private-dns-server.me/dns-query')
}
})
})
const name = ipns(node, {
routers: [
helia(node.routing)
]
})
const name = ipns(node)

const result = name.resolveDNSLink('some-domain-with-dnslink-entry.com')
```
Expand All @@ -215,23 +197,21 @@ Calling `resolveDNSLink` with the `@helia/ipns` instance:
```TypeScript
// resolve a CID from a TXT record in a DNS zone file, using the default
// resolver for the current platform eg:
// > dig _dnslink.ipfs.io TXT
// > dig _dnslink.ipfs.tech TXT
// ;; ANSWER SECTION:
// _dnslink.ipfs.io. 60 IN TXT "dnslink=/ipns/website.ipfs.io"
// > dig _dnslink.website.ipfs.io TXT
// ;; ANSWER SECTION:
// _dnslink.website.ipfs.io. 60 IN TXT "dnslink=/ipfs/QmWebsite"
// _dnslink.ipfs.tech. 60 IN CNAME _dnslink.ipfs-tech.on.fleek.co.
// _dnslink.ipfs-tech.on.fleek.co. 120 IN TXT "dnslink=/ipfs/bafybe..."

import { createHelia } from 'helia'
import { ipns } from '@helia/ipns'

const node = await createHelia()
const name = ipns(node)

const { answer } = await name.resolveDNSLink('ipfs.io')
const { answer } = await name.resolveDNSLink('blog.ipfs.tech')

console.info(answer)
// { data: '/ipfs/QmWebsite' }
// { data: '/ipfs/bafybe...' }
```

## Example - Using DNS-Over-HTTPS
Expand All @@ -247,8 +227,10 @@ import { createHelia } from 'helia'
import { ipns } from '@helia/ipns'
import { dns } from '@multiformats/dns'
import { dnsOverHttps } from '@multiformats/dns/resolvers'
import type { DefaultLibp2pServices } from 'helia'
import type { Libp2p } from '@libp2p/interface'

const node = await createHelia({
const node = await createHelia<Libp2p<DefaultLibp2pServices>>({
dns: dns({
resolvers: {
'.': dnsOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
Expand All @@ -257,7 +239,7 @@ const node = await createHelia({
})
const name = ipns(node)

const result = await name.resolveDNSLink('ipfs.io')
const result = await name.resolveDNSLink('blog.ipfs.tech')
```

## Example - Using DNS-JSON-Over-HTTPS
Expand All @@ -270,8 +252,10 @@ import { createHelia } from 'helia'
import { ipns } from '@helia/ipns'
import { dns } from '@multiformats/dns'
import { dnsJsonOverHttps } from '@multiformats/dns/resolvers'
import type { DefaultLibp2pServices } from 'helia'
import type { Libp2p } from '@libp2p/interface'

const node = await createHelia({
const node = await createHelia<Libp2p<DefaultLibp2pServices>>({
dns: dns({
resolvers: {
'.': dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
Expand All @@ -280,7 +264,7 @@ const node = await createHelia({
})
const name = ipns(node)

const result = await name.resolveDNSLink('ipfs.io')
const result = await name.resolveDNSLink('blog.ipfs.tech')
```

## Example - Republishing an existing IPNS record
Expand Down
9 changes: 8 additions & 1 deletion packages/ipns/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"doc-check": "aegir doc-check",
"build": "aegir build",
"docs": "aegir docs",
"generate": "protons ./src/pb/metadata.proto",
"test": "aegir test",
"test:chrome": "aegir test -t browser --cov",
"test:chrome-webworker": "aegir test -t webworker",
Expand All @@ -79,24 +80,30 @@
"test:electron-main": "aegir test -t electron-main"
},
"dependencies": {
"@libp2p/crypto": "^5.1.7",
"@helia/interface": "^5.4.0",
"@libp2p/interface": "^2.2.1",
"@libp2p/kad-dht": "^15.0.2",
"@libp2p/keychain": "^5.2.8",
"@libp2p/logger": "^5.1.4",
"@libp2p/peer-id": "^5.1.0",
"@libp2p/utils": "^6.7.1",
"@multiformats/dns": "^1.0.6",
"helia": "^5.4.2",
"interface-datastore": "^8.3.1",
"ipns": "^10.0.0",
"multiformats": "^13.3.1",
"progress-events": "^1.0.1",
"protons-runtime": "^5.5.0",
"uint8arraylist": "^2.4.8",
"uint8arrays": "^5.1.0"
},
"devDependencies": {
"@libp2p/crypto": "^5.0.7",
"@types/dns-packet": "^5.6.5",
"aegir": "^47.0.7",
"datastore-core": "^10.0.2",
"it-drain": "^3.0.7",
"protons": "^7.6.1",
"sinon": "^20.0.0",
"sinon-ts": "^2.0.0"
},
Expand Down
Loading
Loading