Initialize repo for strfry nostr relay Ansible Role.
This commit is contained in:
commit
ccb50b1ac2
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
17
LICENSE
Normal file
17
LICENSE
Normal file
@ -0,0 +1,17 @@
|
||||
MIT No Attribution License
|
||||
|
||||
Copyright (c) 2023 Brian Lee
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Ansible Role: strfry
|
||||
|
||||
This Ansible Role builds and installs [strfry](https://github.com/hoytech/strfry).
|
||||
|
||||
Future role improvements:
|
||||
|
||||
* The git tasks in this role are not yet idempotent due to handling a submodule.
|
||||
* The installation process could be made to check if strfry is already running and perform a zero-downtime upgrade.
|
||||
|
||||
## Requirements
|
||||
|
||||
None.
|
||||
|
||||
## Role Variables
|
||||
|
||||
```yaml
|
||||
strfry_version: beta # git repository branch or release tag
|
||||
strfry_make_jobs: "{{ ansible_processor_cores }}" # number of CPUs to build with
|
||||
strfry_skip_config: False
|
||||
```
|
||||
|
||||
See `defaults/main.yml`
|
||||
|
||||
If you are not using the `beta` branch/version, you should override the template with your own by setting `strfry_skip_config` to true and manage the configuration manually.
|
||||
|
||||
For more configuration info, see the relevant upstream [configuration example](https://github.com/hoytech/strfry/blob/beta/strfry.conf) for your branch/version.
|
||||
|
||||
## Example Playbook
|
||||
|
||||
```yaml
|
||||
- hosts: all
|
||||
become: true
|
||||
roles:
|
||||
- role: bleetube.strfry
|
||||
- role: nginxinc.nginx_core.nginx
|
||||
tasks:
|
||||
- import_tasks: nginx_conf.yml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If `make` fails, try running on a single core:
|
||||
|
||||
```shell
|
||||
ansible-playbook playbooks/strfry/main.yml -e 'strfry_make_jobs=1'
|
||||
```
|
123
defaults/main.yml
Normal file
123
defaults/main.yml
Normal file
@ -0,0 +1,123 @@
|
||||
---
|
||||
strfry_version: beta
|
||||
strfry_binary_path: /usr/local/bin/strfry
|
||||
strfry_make_jobs: "{{ ansible_processor_cores }}"
|
||||
strfry_skip_config: False
|
||||
strfry_system_group: strfry
|
||||
strfry_system_user: strfry
|
||||
|
||||
strfry_data_path: /var/lib/strfry
|
||||
strfry_db: "./strfry-db/" # Becomes /var/lib/strfry/strfry-db
|
||||
|
||||
strfry_dbParams:
|
||||
# Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required)
|
||||
maxreaders: 256
|
||||
|
||||
# Size of mmap() to use when loading LMDB (default is 10TB, does *not* correspond to disk-space used) (restart required)
|
||||
mapsize: 10995116277760
|
||||
|
||||
strfry_relay:
|
||||
# Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)
|
||||
bind: "127.0.0.1"
|
||||
|
||||
# Port to open for the nostr websocket protocol (restart required)
|
||||
port: 7777
|
||||
|
||||
# Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required)
|
||||
nofiles: 1000000
|
||||
|
||||
# HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case)
|
||||
realIpHeader: ""
|
||||
|
||||
info:
|
||||
# NIP-11: Name of this server. Short/descriptive (< 30 characters)
|
||||
name: "strfry default"
|
||||
|
||||
# NIP-11: Detailed information about relay, free-form
|
||||
description: "This is a strfry instance."
|
||||
|
||||
# NIP-11: Administrative nostr pubkey, for contact purposes
|
||||
pubkey: "unset"
|
||||
|
||||
# NIP-11: Alternative administrative contact (email, website, etc)
|
||||
contact: "unset"
|
||||
|
||||
# Maximum accepted incoming websocket frame size (should be larger than max event and yesstr msg) (restart required)
|
||||
maxWebsocketPayloadSize: 131072
|
||||
|
||||
# Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required)
|
||||
autoPingSeconds: 55
|
||||
|
||||
# If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy)
|
||||
enableTcpKeepalive: false
|
||||
|
||||
# How much uninterrupted CPU time a REQ query should get during its DB scan
|
||||
queryTimesliceBudgetMicroseconds: 10000
|
||||
|
||||
# Maximum records that can be returned per filter
|
||||
maxFilterLimit: 500
|
||||
|
||||
# Maximum number of subscriptions (concurrent REQs) a connection can have open at any time
|
||||
maxSubsPerConnection: 20
|
||||
|
||||
writePolicy:
|
||||
# If non-empty, path to an executable script that implements the writePolicy plugin logic
|
||||
plugin: ""
|
||||
|
||||
# Number of seconds to search backwards for lookback events when starting the writePolicy plugin (0 for no lookback)
|
||||
lookbackSeconds: 0
|
||||
|
||||
compression:
|
||||
# Use permessage-deflate compression if supported by client. Reduces bandwidth, but slight increase in CPU (restart required)
|
||||
enabled: "true"
|
||||
|
||||
# Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required)
|
||||
slidingWindow: "true"
|
||||
|
||||
logging:
|
||||
# Dump all incoming messages
|
||||
dumpInAll: "false"
|
||||
|
||||
# Dump all incoming EVENT messages
|
||||
dumpInEvents: "false"
|
||||
|
||||
# Dump all incoming REQ/CLOSE messages
|
||||
dumpInReqs: "false"
|
||||
|
||||
# Log performance metrics for initial REQ database scans
|
||||
dbScanPerf: "false"
|
||||
|
||||
numThreads:
|
||||
# Ingester threads: route incoming requests, validate events/sigs (restart required)
|
||||
ingester: 3
|
||||
|
||||
# reqWorker threads: Handle initial DB scan for events (restart required)
|
||||
reqWorker: 3
|
||||
|
||||
# reqMonitor threads: Handle filtering of new events (restart required)
|
||||
reqMonitor: 3
|
||||
|
||||
# yesstr threads: Experimental yesstr protocol (restart required)
|
||||
yesstr: 1
|
||||
|
||||
strfry_events:
|
||||
# Maximum size of normalised JSON, in bytes
|
||||
maxEventSize: 65536
|
||||
|
||||
# Events newer than this will be rejected
|
||||
rejectEventsNewerThanSeconds: 900
|
||||
|
||||
# Events older than this will be rejected
|
||||
rejectEventsOlderThanSeconds: 94608000
|
||||
|
||||
# Ephemeral events older than this will be rejected
|
||||
rejectEphemeralEventsOlderThanSeconds: 60
|
||||
|
||||
# Ephemeral events will be deleted from the DB when older than this
|
||||
ephemeralEventsLifetimeSeconds: 300
|
||||
|
||||
# Maximum number of tags allowed
|
||||
maxNumTags: 2000
|
||||
|
||||
# Maximum size for tag values, in bytes
|
||||
maxTagValSize: 1024
|
7
handlers/main.yml
Normal file
7
handlers/main.yml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: restart strfry
|
||||
ansible.builtin.service:
|
||||
name: strfry
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
become: true
|
2
meta/main.yml
Normal file
2
meta/main.yml
Normal file
@ -0,0 +1,2 @@
|
||||
---
|
||||
dependencies: []
|
31
tasks/build.yml
Normal file
31
tasks/build.yml
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
- name: Avoid running as root
|
||||
ansible.builtin.pause:
|
||||
prompt: "Please run this role as a regular user with sudo/doas privileges. Or press enter to continue anyway (not recommended)."
|
||||
when: ansible_user_id == 'root'
|
||||
|
||||
- name: Clone git repository
|
||||
ansible.builtin.git:
|
||||
repo: https://github.com/hoytech/strfry.git
|
||||
dest: "{{ ansible_env.HOME }}/src/strfry"
|
||||
version: "{{ strfry_version }}"
|
||||
force: yes # required to avoid weird error when updating submodules via Ansible
|
||||
register: git_repository
|
||||
|
||||
- name: Update git submodule (golpe)
|
||||
ansible.builtin.command:
|
||||
cmd: git submodule update --init
|
||||
chdir: "{{ ansible_env.HOME }}/src/strfry"
|
||||
when: git_repository.changed
|
||||
|
||||
- name: Run make setup-golpe
|
||||
ansible.builtin.command:
|
||||
cmd: make setup-golpe
|
||||
chdir: "{{ ansible_env.HOME }}/src/strfry"
|
||||
when: git_repository.changed
|
||||
|
||||
- name: Build strfry
|
||||
ansible.builtin.command:
|
||||
cmd: "make -j{{ strfry_make_jobs|default(1) }}"
|
||||
chdir: "{{ ansible_env.HOME }}/src/strfry"
|
||||
when: git_repository.changed
|
51
tasks/install.yml
Normal file
51
tasks/install.yml
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
- name: Install strfry binary
|
||||
ansible.builtin.copy:
|
||||
src: "{{ ansible_env.HOME }}/src/strfry/strfry"
|
||||
dest: "{{ strfry_binary_path }}"
|
||||
mode: 0755
|
||||
remote_src: true
|
||||
become: true
|
||||
|
||||
- name: Setup strfry service unit
|
||||
ansible.builtin.template:
|
||||
src: strfry.service
|
||||
dest: /etc/systemd/system/strfry.service
|
||||
become: true
|
||||
notify: restart strfry
|
||||
|
||||
- name: Ensure strfry is enabled on boot
|
||||
ansible.builtin.service:
|
||||
name: strfry
|
||||
enabled: true
|
||||
state: started
|
||||
become: true
|
||||
|
||||
- name: Configure strfry
|
||||
ansible.builtin.template:
|
||||
src: strfry.conf
|
||||
dest: "{{ strfry_data_path }}/strfry.conf"
|
||||
owner: "{{ strfry_system_user }}"
|
||||
group: "{{ strfry_system_group }}"
|
||||
when: not strfry_skip_config
|
||||
become: true
|
||||
tags: config
|
||||
notify: restart strfry
|
||||
|
||||
- name: Ensure the configured database directory exists.
|
||||
ansible.builtin.file:
|
||||
path: "{{ strfry_db }}"
|
||||
state: directory
|
||||
owner: "{{ strfry_system_user }}"
|
||||
group: "{{ strfry_system_group }}"
|
||||
when: strfry_db.startswith('/')
|
||||
become: true
|
||||
|
||||
- name: Ensure the configured database directory exists.
|
||||
ansible.builtin.file:
|
||||
path: "{{ strfry_data_path }}/{{ strfry_db }}"
|
||||
state: directory
|
||||
owner: "{{ strfry_system_user }}"
|
||||
group: "{{ strfry_system_group }}"
|
||||
when: not strfry_db.startswith('/')
|
||||
become: true
|
24
tasks/main.yml
Normal file
24
tasks/main.yml
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Load a variable file based on the OS type.
|
||||
include_vars: "{{ item }}"
|
||||
with_first_found:
|
||||
- "{{ ansible_distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_distribution }}.yml"
|
||||
- "{{ ansible_os_family }}.yml"
|
||||
|
||||
- name: Install compiler dependencies (Debian)
|
||||
ansible.builtin.package:
|
||||
name: "{{ compiler_packages }}"
|
||||
state: present
|
||||
when: ansible_os_family == 'Debian'
|
||||
become: true
|
||||
|
||||
- name: Set up strfry user
|
||||
import_tasks: setup-user.yml
|
||||
|
||||
#- name: Run build tasks
|
||||
# import_tasks: build.yml
|
||||
# tags: build
|
||||
|
||||
- name: Install strfry
|
||||
import_tasks: install.yml
|
39
tasks/setup-user.yml
Normal file
39
tasks/setup-user.yml
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
- name: Get nologin path for strfry user
|
||||
ansible.builtin.find:
|
||||
paths:
|
||||
- /bin
|
||||
- /sbin
|
||||
- /usr/bin
|
||||
- /usr/sbin
|
||||
patterns: nologin
|
||||
register: nologin_bin
|
||||
become: true
|
||||
|
||||
- name: Create the strfry group
|
||||
ansible.builtin.group:
|
||||
name: "{{ strfry_system_group }}"
|
||||
state: present
|
||||
system: true
|
||||
when: strfry_system_group != "root"
|
||||
become: true
|
||||
|
||||
- name: Create the strfry system user
|
||||
ansible.builtin.user:
|
||||
name: "{{ strfry_system_user }}"
|
||||
groups: "{{ strfry_system_group }}"
|
||||
shell: "{{ nologin_bin.files[0].path }}"
|
||||
system: true
|
||||
create_home: false
|
||||
home: "{{ strfry_data_path }}"
|
||||
when: strfry_system_user != "root"
|
||||
become: true
|
||||
|
||||
- name: Ensure strfry_path exists.
|
||||
ansible.builtin.file:
|
||||
path: "{{ strfry_data_path }}"
|
||||
owner: "{{ strfry_system_user }}"
|
||||
group: "{{ strfry_system_group }}"
|
||||
state: directory
|
||||
mode: '0750'
|
||||
become: true
|
129
templates/strfry.conf
Normal file
129
templates/strfry.conf
Normal file
@ -0,0 +1,129 @@
|
||||
##
|
||||
## Default strfry config
|
||||
##
|
||||
|
||||
# Directory that contains the strfry LMDB database (restart required)
|
||||
# Ansible template for strfry.conf
|
||||
|
||||
db = "{{ strfry_db }}"
|
||||
|
||||
dbParams {
|
||||
# Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required)
|
||||
maxreaders = {{ strfry_dbParams.maxreaders }}
|
||||
|
||||
# Size of mmap() to use when loading LMDB (default is 10TB, does *not* correspond to disk-space used) (restart required)
|
||||
mapsize = {{ strfry_dbParams.mapsize }}
|
||||
}
|
||||
|
||||
relay {
|
||||
# Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)
|
||||
bind = "{{ strfry_relay.bind }}"
|
||||
|
||||
# Port to open for the nostr websocket protocol (restart required)
|
||||
port = {{ strfry_relay.port }}
|
||||
|
||||
# Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required)
|
||||
nofiles = {{ strfry_relay.nofiles }}
|
||||
|
||||
# HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case)
|
||||
realIpHeader = "{{ strfry_relay.realIpHeader }}"
|
||||
|
||||
info {
|
||||
# NIP-11: Name of this server. Short/descriptive (< 30 characters)
|
||||
name = "{{ strfry_relay.info.name }}"
|
||||
|
||||
# NIP-11: Detailed information about relay, free-form
|
||||
description = "{{ strfry_relay.info.description }}"
|
||||
|
||||
# NIP-11: Administrative nostr pubkey, for contact purposes
|
||||
pubkey = "{{ strfry_relay.info.pubkey }}"
|
||||
|
||||
# NIP-11: Alternative administrative contact (email, website, etc)
|
||||
contact = "{{ strfry_relay.info.contact }}"
|
||||
}
|
||||
|
||||
# Maximum accepted incoming websocket frame size (should be larger than max event and yesstr msg) (restart required)
|
||||
maxWebsocketPayloadSize = {{ strfry_relay.maxWebsocketPayloadSize }}
|
||||
|
||||
# Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required)
|
||||
autoPingSeconds = {{ strfry_relay.autoPingSeconds }}
|
||||
|
||||
# If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy)
|
||||
enableTcpKeepalive = {{ strfry_relay.enableTcpKeepalive }}
|
||||
|
||||
# How much uninterrupted CPU time a REQ query should get during its DB scan
|
||||
queryTimesliceBudgetMicroseconds = {{ strfry_relay.queryTimesliceBudgetMicroseconds }}
|
||||
|
||||
# Maximum records that can be returned per filter
|
||||
maxFilterLimit = {{ strfry_relay.maxFilterLimit }}
|
||||
|
||||
# Maximum number of subscriptions (concurrent REQs) a connection can have open at any time
|
||||
maxSubsPerConnection = {{ strfry_relay.maxSubsPerConnection }}
|
||||
|
||||
writePolicy {
|
||||
# If non-empty, path to an executable script that implements the writePolicy plugin logic
|
||||
plugin = "{{ strfry_relay.writePolicy.plugin }}"
|
||||
|
||||
# Number of seconds to search backwards for lookback events when starting the writePolicy plugin (0 for no lookback)
|
||||
lookbackSeconds = {{ strfry_relay.writePolicy.lookbackSeconds }}
|
||||
}
|
||||
|
||||
compression {
|
||||
# Use permessage-deflate compression if supported by client. Reduces bandwidth, but slight increase in CPU (restart required)
|
||||
enabled = {{ strfry_relay.compression.enabled }}
|
||||
|
||||
# Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required)
|
||||
slidingWindow = {{ strfry_relay.compression.slidingWindow }}
|
||||
}
|
||||
|
||||
logging {
|
||||
# Dump all incoming messages
|
||||
dumpInAll = {{ strfry_relay.logging.dumpInAll }}
|
||||
|
||||
# Dump all incoming EVENT messages
|
||||
dumpInEvents = {{ strfry_relay.logging.dumpInEvents }}
|
||||
|
||||
# Dump all incoming REQ/CLOSE messages
|
||||
dumpInReqs = {{ strfry_relay.logging.dumpInReqs }}
|
||||
|
||||
# Log performance metrics for initial REQ database scans
|
||||
dbScanPerf = {{ strfry_relay.logging.dbScanPerf }}
|
||||
}
|
||||
|
||||
numThreads {
|
||||
# Ingester threads: route incoming requests, validate events/sigs (restart required)
|
||||
ingester = {{ strfry_relay.numThreads.ingester }}
|
||||
|
||||
# reqWorker threads: Handle initial DB scan for events (restart required)
|
||||
reqWorker = {{ strfry_relay.numThreads.reqWorker }}
|
||||
|
||||
# reqMonitor threads: Handle filtering of new events (restart required)
|
||||
reqMonitor = {{ strfry_relay.numThreads.reqMonitor }}
|
||||
|
||||
# yesstr threads: Experimental yesstr protocol (restart required)
|
||||
yesstr = {{ strfry_relay.numThreads.yesstr }}
|
||||
}
|
||||
}
|
||||
|
||||
events {
|
||||
# Maximum size of normalised JSON, in bytes
|
||||
maxEventSize = {{ strfry_events.maxEventSize }}
|
||||
|
||||
# Events newer than this will be rejected
|
||||
rejectEventsNewerThanSeconds = {{ strfry_events.rejectEventsNewerThanSeconds }}
|
||||
|
||||
# Events older than this will be rejected
|
||||
rejectEventsOlderThanSeconds = {{ strfry_events.rejectEventsOlderThanSeconds }}
|
||||
|
||||
# Ephemeral events older than this will be rejected
|
||||
rejectEphemeralEventsOlderThanSeconds = {{ strfry_events.rejectEphemeralEventsOlderThanSeconds }}
|
||||
|
||||
# Ephemeral events will be deleted from the DB when older than this
|
||||
ephemeralEventsLifetimeSeconds = {{ strfry_events.ephemeralEventsLifetimeSeconds }}
|
||||
|
||||
# Maximum number of tags allowed
|
||||
maxNumTags = {{ strfry_events.maxNumTags }}
|
||||
|
||||
# Maximum size for tag values, in bytes
|
||||
maxTagValSize = {{ strfry_events.maxTagValSize }}
|
||||
}
|
22
templates/strfry.service
Normal file
22
templates/strfry.service
Normal file
@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=strfry nostr relay
|
||||
|
||||
[Service]
|
||||
User={{ strfry_system_user }}
|
||||
Group={{ strfry_system_group }}
|
||||
WorkingDirectory={{ strfry_data_path }}
|
||||
ExecStart={{ strfry_binary_path }} --config=strfry.conf relay
|
||||
Restart=on-failure
|
||||
RestartSec=15
|
||||
LimitNOFILE=65536
|
||||
|
||||
# Hardening
|
||||
ProtectHome=yes
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=full
|
||||
|
||||
[Journal]
|
||||
MaxRetentionSec=2h
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
11
vars/Archlinux.yml
Normal file
11
vars/Archlinux.yml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
compiler_packages:
|
||||
- base-devel
|
||||
- flatbuffers
|
||||
- git
|
||||
- lmdb
|
||||
- openssl
|
||||
- perl-regexp-grammars
|
||||
- perl-template-toolkit
|
||||
- perl-yaml
|
||||
- zlib
|
13
vars/Debian.yml
Normal file
13
vars/Debian.yml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
compiler_packages:
|
||||
- build-essential
|
||||
- git
|
||||
- libflatbuffers-dev
|
||||
- liblmdb-dev
|
||||
- libregexp-grammars-perl
|
||||
- libsecp256k1-dev
|
||||
- libssl-dev
|
||||
- libtemplate-perl
|
||||
- libyaml-perl
|
||||
- libzstd-dev
|
||||
- zlib1g-dev
|
9
vars/RedHat.yml
Normal file
9
vars/RedHat.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
compiler_packages:
|
||||
- @development-tools
|
||||
- git
|
||||
- lmdb-devel
|
||||
- openssl-devel
|
||||
- perl-Template-Toolkit
|
||||
- perl-YAML
|
||||
- zlib-devel
|
14
vars/Ubuntu.yml
Normal file
14
vars/Ubuntu.yml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
compiler_packages:
|
||||
- build-essential
|
||||
- git
|
||||
- libflatbuffers-dev
|
||||
- liblmdb-dev
|
||||
- libregexp-grammars-perl
|
||||
- libsecp256k1-dev
|
||||
- libssl-dev
|
||||
- libtemplate-perl
|
||||
- libyaml-perl
|
||||
- libzstd-dev
|
||||
- zlib1g-dev
|
||||
- libb2-dev # Ubuntu 22.04
|
Loading…
Reference in New Issue
Block a user