Merge branch 'master' into live
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
b6bcfa2ee3
@ -7,7 +7,7 @@ Sidekiq::Testing.inline! do
|
|||||||
|
|
||||||
puts "Create user: admin"
|
puts "Create user: admin"
|
||||||
|
|
||||||
CreateAccount.call(account: {
|
UserManager::CreateAccount.call(account: {
|
||||||
username: "admin", domain: "kosmos.org", email: "admin@example.com",
|
username: "admin", domain: "kosmos.org", email: "admin@example.com",
|
||||||
password: "admin is admin", confirmed: true
|
password: "admin is admin", confirmed: true
|
||||||
})
|
})
|
||||||
@ -20,7 +20,7 @@ Sidekiq::Testing.inline! do
|
|||||||
email = Faker::Internet.unique.email
|
email = Faker::Internet.unique.email
|
||||||
next if username.length < 3
|
next if username.length < 3
|
||||||
|
|
||||||
CreateAccount.call(account: {
|
UserManager::CreateAccount.call(account: {
|
||||||
username: username, domain: "kosmos.org", email: email,
|
username: username, domain: "kosmos.org", email: email,
|
||||||
password: "user is user", confirmed: true
|
password: "user is user", confirmed: true
|
||||||
})
|
})
|
||||||
|
@ -111,7 +111,7 @@ services:
|
|||||||
- redis
|
- redis
|
||||||
|
|
||||||
strfry:
|
strfry:
|
||||||
image: gitea.kosmos.org/kosmos/strfry-deno:1.1.1
|
image: gitea.kosmos.org/kosmos/strfry-deno:2.0.0
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker/strfry/strfry.conf:/etc/strfry.conf
|
- ./docker/strfry/strfry.conf:/etc/strfry.conf
|
||||||
- ./extras/strfry:/opt/strfry
|
- ./extras/strfry:/opt/strfry
|
||||||
|
57
docs/dev/nostr.md
Normal file
57
docs/dev/nostr.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Nostr
|
||||||
|
|
||||||
|
## strfry
|
||||||
|
|
||||||
|
The `extras/strfry` directory contains code to integrate [strfry][1] with
|
||||||
|
akkounts, so that notes published to the relay have to be authored by (or in
|
||||||
|
some cases just related to) local users who have verified their Nostr public
|
||||||
|
key.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
[Deno](https://deno.com/) needs to be installed on the machine that you run
|
||||||
|
strfry on.
|
||||||
|
|
||||||
|
We provide a Docker image with recent strfry and Deno builds:
|
||||||
|
|
||||||
|
https://gitea.kosmos.org/kosmos/-/packages/container/strfry-deno/
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
You can use either environment variables (see e.g. the `strfry` service in
|
||||||
|
`docker-compose-yml`) or a local `.env` file in the same working directory
|
||||||
|
that you place the extra files in (e.g. `/opt/strfry`).
|
||||||
|
|
||||||
|
In your `strfry.conf`, configure `strfry-policy.ts` as the write policy, like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
writePolicy {
|
||||||
|
plugin = "/opt/strfry/strfry-policy.ts"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All dependencies will be downloaded and cached automatically when the plugin is
|
||||||
|
called for the first time.
|
||||||
|
|
||||||
|
### Manual tasks
|
||||||
|
|
||||||
|
You can sync all notes authored by local users (any account that has verified
|
||||||
|
their Nostr pubkey with akkounts) from a remote [strfry][1] relay via negentropy
|
||||||
|
sync:
|
||||||
|
|
||||||
|
deno run -A /opt/strfry/strfry-sync.ts wss://nostr.kosmos.org
|
||||||
|
|
||||||
|
Or, in the running container when using Docker Compose:
|
||||||
|
|
||||||
|
docker compose exec strfry deno run -A /opt/strfry/strfry-sync.ts wss://nostr.kosmos.org
|
||||||
|
|
||||||
|
The `strfry` service container also exposes the local relay on your local host
|
||||||
|
on port 4777.
|
||||||
|
|
||||||
|
[nak](https://github.com/fiatjaf/nak) is a helpful tool for manual Nostr tasks.
|
||||||
|
Here's how you can grab a note by its event ID from a remote relay and publish
|
||||||
|
it to your local strfry for example:
|
||||||
|
|
||||||
|
nak req -i 0fb010192685b86b0810b3de3706fbbf3b8c1db30b14533094a2b9700c820cdc nostr.kosmos.org | nak event ws://localhost:4777
|
||||||
|
|
||||||
|
[1]: https://github.com/hoytech/strfry
|
236
extras/strfry/deno.lock
generated
236
extras/strfry/deno.lock
generated
@ -1,101 +1,231 @@
|
|||||||
{
|
{
|
||||||
"version": "3",
|
"version": "4",
|
||||||
"packages": {
|
|
||||||
"specifiers": {
|
"specifiers": {
|
||||||
"jsr:@nostr/tools@^2.3.1": "jsr:@nostr/tools@2.3.1",
|
"jsr:@nostr/tools@*": "2.3.1",
|
||||||
"npm:@noble/ciphers@^0.5.1": "npm:@noble/ciphers@0.5.3",
|
"jsr:@nostr/tools@^2.3.1": "2.3.1",
|
||||||
"npm:@noble/curves@1.2.0": "npm:@noble/curves@1.2.0",
|
"jsr:@nostrify/nostrify@0.36": "0.36.2",
|
||||||
"npm:@noble/hashes@1.3.1": "npm:@noble/hashes@1.3.1",
|
"jsr:@nostrify/policies@*": "0.36.1",
|
||||||
"npm:@scure/base@1.1.1": "npm:@scure/base@1.1.1",
|
"jsr:@nostrify/strfry@*": "0.2.1",
|
||||||
"npm:ldapts": "npm:ldapts@7.0.12"
|
"jsr:@nostrify/types@0.35": "0.35.0",
|
||||||
|
"jsr:@nostrify/types@0.36": "0.36.0",
|
||||||
|
"jsr:@std/bytes@^1.0.5": "1.0.5",
|
||||||
|
"jsr:@std/encoding@~0.224.1": "0.224.3",
|
||||||
|
"jsr:@std/json@^1.0.1": "1.0.1",
|
||||||
|
"jsr:@std/streams@^1.0.7": "1.0.9",
|
||||||
|
"jsr:@std/streams@^1.0.8": "1.0.9",
|
||||||
|
"npm:@noble/ciphers@~0.5.1": "0.5.3",
|
||||||
|
"npm:@noble/curves@1.2.0": "1.2.0",
|
||||||
|
"npm:@noble/hashes@1.3.1": "1.3.1",
|
||||||
|
"npm:@scure/base@1.1.1": "1.1.1",
|
||||||
|
"npm:@scure/bip32@^1.4.0": "1.6.2",
|
||||||
|
"npm:@scure/bip39@^1.3.0": "1.5.4",
|
||||||
|
"npm:ldapts@*": "7.0.12",
|
||||||
|
"npm:lru-cache@^10.2.0": "10.4.3",
|
||||||
|
"npm:nostr-tools@^2.7.0": "2.12.0",
|
||||||
|
"npm:websocket-ts@^2.1.5": "2.2.1",
|
||||||
|
"npm:zod@^3.23.8": "3.24.2"
|
||||||
},
|
},
|
||||||
"jsr": {
|
"jsr": {
|
||||||
"@nostr/tools@2.3.1": {
|
"@nostr/tools@2.3.1": {
|
||||||
"integrity": "af01dc45cb28784c584d7a0699707196f397bcc53946efa582a01b11ddde4d61",
|
"integrity": "af01dc45cb28784c584d7a0699707196f397bcc53946efa582a01b11ddde4d61",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"npm:@noble/ciphers@^0.5.1",
|
"npm:@noble/ciphers",
|
||||||
"npm:@noble/curves@1.2.0",
|
"npm:@noble/curves",
|
||||||
"npm:@noble/hashes@1.3.1",
|
"npm:@noble/hashes",
|
||||||
"npm:@scure/base@1.1.1"
|
"npm:@scure/base"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@nostrify/nostrify@0.36.2": {
|
||||||
|
"integrity": "cc4787ca170b623a2e5dfed1baa4426077daa6143af728ea7dd325d58f4d04d6",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@nostrify/types@0.35",
|
||||||
|
"jsr:@std/encoding",
|
||||||
|
"npm:@scure/bip32",
|
||||||
|
"npm:@scure/bip39",
|
||||||
|
"npm:lru-cache",
|
||||||
|
"npm:nostr-tools",
|
||||||
|
"npm:websocket-ts",
|
||||||
|
"npm:zod"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@nostrify/policies@0.36.1": {
|
||||||
|
"integrity": "6d59af115a687fcd18b6caebab0e4f50ee6cdb0aafa2aacd0aec2065021275b4",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@nostrify/nostrify",
|
||||||
|
"jsr:@nostrify/types@0.35",
|
||||||
|
"npm:nostr-tools"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@nostrify/strfry@0.2.1": {
|
||||||
|
"integrity": "be437b13f49e6564e557da23072bf642723a603568f672543a64d9fda6663432",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@nostrify/types@0.36",
|
||||||
|
"jsr:@std/json",
|
||||||
|
"jsr:@std/streams@^1.0.8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@nostrify/types@0.35.0": {
|
||||||
|
"integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6"
|
||||||
|
},
|
||||||
|
"@nostrify/types@0.36.0": {
|
||||||
|
"integrity": "b3413467debcbd298d217483df4e2aae6c335a34765c90ac7811cf7c637600e7"
|
||||||
|
},
|
||||||
|
"@std/bytes@1.0.5": {
|
||||||
|
"integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e"
|
||||||
|
},
|
||||||
|
"@std/encoding@0.224.3": {
|
||||||
|
"integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf"
|
||||||
|
},
|
||||||
|
"@std/json@1.0.1": {
|
||||||
|
"integrity": "1f0f70737e8827f9acca086282e903677bc1bb0c8ffcd1f21bca60039563049f",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/streams@^1.0.7"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/streams@1.0.9": {
|
||||||
|
"integrity": "a9d26b1988cdd7aa7b1f4b51e1c36c1557f3f252880fa6cc5b9f37078b1a5035",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
"@noble/ciphers@0.5.3": {
|
"@noble/ciphers@0.5.3": {
|
||||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="
|
||||||
"dependencies": {}
|
},
|
||||||
|
"@noble/curves@1.1.0": {
|
||||||
|
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/hashes@1.3.1"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"@noble/curves@1.2.0": {
|
"@noble/curves@1.2.0": {
|
||||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||||
"dependencies": {
|
"dependencies": [
|
||||||
"@noble/hashes": "@noble/hashes@1.3.2"
|
"@noble/hashes@1.3.2"
|
||||||
}
|
]
|
||||||
|
},
|
||||||
|
"@noble/curves@1.8.2": {
|
||||||
|
"integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/hashes@1.7.2"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"@noble/hashes@1.3.1": {
|
"@noble/hashes@1.3.1": {
|
||||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="
|
||||||
"dependencies": {}
|
|
||||||
},
|
},
|
||||||
"@noble/hashes@1.3.2": {
|
"@noble/hashes@1.3.2": {
|
||||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
|
||||||
"dependencies": {}
|
},
|
||||||
|
"@noble/hashes@1.7.2": {
|
||||||
|
"integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="
|
||||||
},
|
},
|
||||||
"@scure/base@1.1.1": {
|
"@scure/base@1.1.1": {
|
||||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
|
||||||
"dependencies": {}
|
},
|
||||||
|
"@scure/base@1.2.4": {
|
||||||
|
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ=="
|
||||||
|
},
|
||||||
|
"@scure/bip32@1.3.1": {
|
||||||
|
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/curves@1.1.0",
|
||||||
|
"@noble/hashes@1.3.2",
|
||||||
|
"@scure/base@1.1.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@scure/bip32@1.6.2": {
|
||||||
|
"integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/curves@1.8.2",
|
||||||
|
"@noble/hashes@1.7.2",
|
||||||
|
"@scure/base@1.2.4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@scure/bip39@1.2.1": {
|
||||||
|
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/hashes@1.3.2",
|
||||||
|
"@scure/base@1.1.1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@scure/bip39@1.5.4": {
|
||||||
|
"integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/hashes@1.7.2",
|
||||||
|
"@scure/base@1.2.4"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"@types/asn1@0.2.4": {
|
"@types/asn1@0.2.4": {
|
||||||
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
"integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==",
|
||||||
"dependencies": {
|
"dependencies": [
|
||||||
"@types/node": "@types/node@18.16.19"
|
"@types/node"
|
||||||
}
|
]
|
||||||
},
|
},
|
||||||
"@types/node@18.16.19": {
|
"@types/node@18.16.19": {
|
||||||
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
|
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA=="
|
||||||
"dependencies": {}
|
|
||||||
},
|
},
|
||||||
"@types/uuid@9.0.8": {
|
"@types/uuid@9.0.8": {
|
||||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="
|
||||||
"dependencies": {}
|
|
||||||
},
|
},
|
||||||
"asn1@0.2.6": {
|
"asn1@0.2.6": {
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
"dependencies": {
|
"dependencies": [
|
||||||
"safer-buffer": "safer-buffer@2.1.2"
|
"safer-buffer"
|
||||||
}
|
]
|
||||||
},
|
},
|
||||||
"debug@4.3.5": {
|
"debug@4.3.5": {
|
||||||
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
||||||
"dependencies": {
|
"dependencies": [
|
||||||
"ms": "ms@2.1.2"
|
"ms"
|
||||||
}
|
]
|
||||||
},
|
},
|
||||||
"ldapts@7.0.12": {
|
"ldapts@7.0.12": {
|
||||||
"integrity": "sha512-orwgIejUi/ZyGah9y8jWZmFUg8Ci5M8WAv0oZjSf3MVuk1sRBdor9Qy1ttGHbYpWj96HXKFunQ8AYZ8WWGp17g==",
|
"integrity": "sha512-orwgIejUi/ZyGah9y8jWZmFUg8Ci5M8WAv0oZjSf3MVuk1sRBdor9Qy1ttGHbYpWj96HXKFunQ8AYZ8WWGp17g==",
|
||||||
"dependencies": {
|
"dependencies": [
|
||||||
"@types/asn1": "@types/asn1@0.2.4",
|
"@types/asn1",
|
||||||
"@types/uuid": "@types/uuid@9.0.8",
|
"@types/uuid",
|
||||||
"asn1": "asn1@0.2.6",
|
"asn1",
|
||||||
"debug": "debug@4.3.5",
|
"debug",
|
||||||
"strict-event-emitter-types": "strict-event-emitter-types@2.0.0",
|
"strict-event-emitter-types",
|
||||||
"uuid": "uuid@9.0.1"
|
"uuid"
|
||||||
}
|
]
|
||||||
|
},
|
||||||
|
"lru-cache@10.4.3": {
|
||||||
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
||||||
},
|
},
|
||||||
"ms@2.1.2": {
|
"ms@2.1.2": {
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dependencies": {}
|
},
|
||||||
|
"nostr-tools@2.12.0": {
|
||||||
|
"integrity": "sha512-pUWEb020gTvt1XZvTa8AKNIHWFapjsv2NKyk43Ez2nnvz6WSXsrTFE0XtkNLSRBjPn6EpxumKeNiVzLz74jNSA==",
|
||||||
|
"dependencies": [
|
||||||
|
"@noble/ciphers",
|
||||||
|
"@noble/curves@1.2.0",
|
||||||
|
"@noble/hashes@1.3.1",
|
||||||
|
"@scure/base@1.1.1",
|
||||||
|
"@scure/bip32@1.3.1",
|
||||||
|
"@scure/bip39@1.2.1",
|
||||||
|
"nostr-wasm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nostr-wasm@0.1.0": {
|
||||||
|
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="
|
||||||
},
|
},
|
||||||
"safer-buffer@2.1.2": {
|
"safer-buffer@2.1.2": {
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
"dependencies": {}
|
|
||||||
},
|
},
|
||||||
"strict-event-emitter-types@2.0.0": {
|
"strict-event-emitter-types@2.0.0": {
|
||||||
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==",
|
"integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
|
||||||
"dependencies": {}
|
|
||||||
},
|
},
|
||||||
"uuid@9.0.1": {
|
"uuid@9.0.1": {
|
||||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
|
||||||
"dependencies": {}
|
},
|
||||||
}
|
"websocket-ts@2.2.1": {
|
||||||
|
"integrity": "sha512-YKPDfxlK5qOheLZ2bTIiktZO1bpfGdNCPJmTEaPW7G9UXI1GKjDdeacOrsULUS000OPNxDVOyAuKLuIWPqWM0Q=="
|
||||||
|
},
|
||||||
|
"zod@3.24.2": {
|
||||||
|
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { IterablePubkeys, Policy } from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts';
|
import { NostrEvent, NostrRelayInfo, NostrRelayOK, NPolicy } from 'jsr:@nostrify/types@^0.35.0';
|
||||||
|
import { nip57 } from 'jsr:@nostr/tools';
|
||||||
import { Client } from 'npm:ldapts';
|
import { Client } from 'npm:ldapts';
|
||||||
import { nip57 } from '@nostr/tools';
|
|
||||||
|
|
||||||
interface LdapConfig {
|
export interface LdapConfig {
|
||||||
url: string;
|
url: string;
|
||||||
bindDN: string;
|
bindDN: string;
|
||||||
password: string;
|
password: string;
|
||||||
@ -10,16 +10,17 @@ interface LdapConfig {
|
|||||||
whitelistPubkeys?: IterablePubkeys;
|
whitelistPubkeys?: IterablePubkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ldapPolicy: Policy<LdapConfig> = async (msg, opts) => {
|
export class LdapPolicy implements NPolicy {
|
||||||
const client = new Client({ url: opts.url });
|
constructor(private opts: LdapConfig) {}
|
||||||
const { kind, tags } = msg.event;
|
|
||||||
let { pubkey } = msg.event;
|
|
||||||
let out = { id: msg.event.id }
|
|
||||||
|
|
||||||
if (opts.whitelistPubkeys.includes(pubkey)) {
|
// deno-lint-ignore require-await
|
||||||
out['action'] = 'accept';
|
async call(event: NostrEvent): Promise<NostrRelayOK> {
|
||||||
out['msg'] = '';
|
const client = new Client({ url: this.opts.url });
|
||||||
return out;
|
const { id, kind, tags } = event;
|
||||||
|
let { pubkey } = event;
|
||||||
|
|
||||||
|
if (this.opts.whitelistPubkeys.includes(pubkey)) {
|
||||||
|
return ['OK', id, true, ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zap receipt
|
// Zap receipt
|
||||||
@ -28,9 +29,7 @@ const ldapPolicy: Policy<LdapConfig> = async (msg, opts) => {
|
|||||||
const invalidZapRequestMsg = 'Zap receipts must contain a valid zap request from a relay member';
|
const invalidZapRequestMsg = 'Zap receipts must contain a valid zap request from a relay member';
|
||||||
|
|
||||||
if (typeof descriptionTag === 'undefined') {
|
if (typeof descriptionTag === 'undefined') {
|
||||||
out['action'] = 'reject';
|
return ['OK', id, false, invalidZapRequestMsg];
|
||||||
out['msg'] = invalidZapRequestMsg;
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const zapRequestJSON = descriptionTag[1];
|
const zapRequestJSON = descriptionTag[1];
|
||||||
@ -43,35 +42,41 @@ const ldapPolicy: Policy<LdapConfig> = async (msg, opts) => {
|
|||||||
if (validationResult === null) {
|
if (validationResult === null) {
|
||||||
pubkey = JSON.parse(zapRequestJSON).pubkey;
|
pubkey = JSON.parse(zapRequestJSON).pubkey;
|
||||||
} else {
|
} else {
|
||||||
out['action'] = 'reject';
|
return ['OK', id, false, invalidZapRequestMsg];
|
||||||
out['msg'] = invalidZapRequestMsg;
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const out = { accept: true, msg: ''};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.bind(opts.bindDN, opts.password);
|
await client.bind(this.opts.bindDN, this.opts.password);
|
||||||
|
|
||||||
const { searchEntries } = await client.search(opts.searchDN, {
|
const { searchEntries } = await client.search(this.opts.searchDN, {
|
||||||
filter: `(nostrKey=${pubkey})`,
|
filter: `(nostrKey=${pubkey})`,
|
||||||
attributes: ['nostrKey']
|
attributes: ['nostrKey']
|
||||||
});
|
});
|
||||||
const memberKey = searchEntries[0]?.nostrKey;
|
const memberKey = searchEntries[0]?.nostrKey;
|
||||||
|
|
||||||
if (memberKey === pubkey) {
|
if (memberKey === pubkey) {
|
||||||
out['action'] = 'accept';
|
out['accept'] = true;
|
||||||
out['msg'] = '';
|
|
||||||
} else {
|
} else {
|
||||||
out['action'] = 'reject';
|
out['accept'] = false;
|
||||||
out['msg'] = 'Only members can publish notes on this relay';
|
out['msg'] = 'Only members can publish notes on this relay';
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
out['action'] = 'reject';
|
out['accept'] = false;
|
||||||
out['msg'] = 'Auth service temporarily unavailable';
|
out['msg'] = 'Auth service temporarily unavailable';
|
||||||
} finally {
|
} finally {
|
||||||
await client.unbind();
|
await client.unbind();
|
||||||
return out;
|
return ['OK', id, out['accept'], out['msg']];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export default ldapPolicy;
|
get info(): NostrRelayInfo {
|
||||||
|
return {
|
||||||
|
limitation: {
|
||||||
|
restricted_writes: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
//bin/true; exec deno run -A "$0" "$@"
|
//bin/true; exec deno run --unstable-kv -A "$0" "$@"
|
||||||
import {
|
import {
|
||||||
antiDuplicationPolicy,
|
AntiDuplicationPolicy,
|
||||||
hellthreadPolicy,
|
HellthreadPolicy,
|
||||||
pipeline,
|
PipePolicy,
|
||||||
rateLimitPolicy,
|
|
||||||
readStdin,
|
readStdin,
|
||||||
writeStdout,
|
writeStdout,
|
||||||
} from 'https://gitlab.com/soapbox-pub/strfry-policies/-/raw/develop/mod.ts';
|
} from 'jsr:@nostrify/policies';
|
||||||
import ldapPolicy from './ldap-policy.ts';
|
import { strfry } from 'jsr:@nostrify/strfry';
|
||||||
|
import { LdapConfig, LdapPolicy } from './ldap-policy.ts';
|
||||||
import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";
|
import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";
|
||||||
|
|
||||||
const dirname = new URL('.', import.meta.url).pathname;
|
const dirname = new URL('.', import.meta.url).pathname;
|
||||||
await load({ envPath: `${dirname}/.env`, export: true });
|
await load({ envPath: `${dirname}/.env`, export: true });
|
||||||
|
|
||||||
const ldapConfig = {
|
const ldapConfig: LdapConfig = {
|
||||||
url: Deno.env.get("LDAP_URL"),
|
url: Deno.env.get("LDAP_URL"),
|
||||||
bindDN: Deno.env.get("LDAP_BIND_DN"),
|
bindDN: Deno.env.get("LDAP_BIND_DN"),
|
||||||
password: Deno.env.get("LDAP_PASSWORD"),
|
password: Deno.env.get("LDAP_PASSWORD"),
|
||||||
@ -22,13 +22,10 @@ const ldapConfig = {
|
|||||||
whitelistPubkeys: Deno.env.get("WHITELIST_PUBKEYS")?.split(',')
|
whitelistPubkeys: Deno.env.get("WHITELIST_PUBKEYS")?.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const msg of readStdin()) {
|
const policy = new PipePolicy([
|
||||||
const result = await pipeline(msg, [
|
new HellthreadPolicy({ limit: 10 }),
|
||||||
[hellthreadPolicy, { limit: 10 }],
|
new AntiDuplicationPolicy({ kv: await Deno.openKv(), expireIn: 60000, minLength: 50 }),
|
||||||
[antiDuplicationPolicy, { ttl: 60000, minLength: 50 }],
|
new LdapPolicy(ldapConfig)
|
||||||
[rateLimitPolicy, { whitelist: ['127.0.0.1'] }],
|
]);
|
||||||
[ldapPolicy, ldapConfig],
|
|
||||||
]);
|
|
||||||
|
|
||||||
writeStdout(result);
|
await strfry(policy);
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user