mirror of
https://github.com/agessaman/meshcore-packet-capture.git
synced 2026-04-20 23:23:37 +00:00
I think the NixOS flake is okay. If it isn't, send a PR. I gave it my best shot.
This commit is contained in:
parent
517659035f
commit
8b3bf8f7e2
6 changed files with 0 additions and 611 deletions
|
|
@ -1,29 +0,0 @@
|
|||
FROM ubuntu:22.04
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
git \
|
||||
xz-utils \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create nixbld group and user for Nix installation
|
||||
RUN groupadd -r nixbld && \
|
||||
for i in $(seq 1 32); do useradd -r -g nixbld -G nixbld -d /var/empty -s /sbin/nologin -c "Nix build user $i" nixbld$i 2>/dev/null || true; done
|
||||
|
||||
# Install Nix (single-user mode for Docker)
|
||||
RUN mkdir -m 0755 -p /nix && \
|
||||
curl -L https://nixos.org/nix/install | sh -s -- --no-daemon
|
||||
|
||||
# Enable flakes
|
||||
RUN mkdir -p /root/.config/nix && \
|
||||
echo "experimental-features = nix-command flakes" >> /root/.config/nix/nix.conf
|
||||
|
||||
# Set up environment
|
||||
ENV PATH="/root/.nix-profile/bin:/nix/var/nix/profiles/default/bin:$PATH"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
# NixOS Support
|
||||
|
||||
This project includes a NixOS service module that allows you to run MeshCore Packet Capture as a systemd service on NixOS.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Recommended Configuration (Let'sMesh Analyzer)
|
||||
|
||||
This configuration uploads packets to both Let's Mesh Analyzer servers (US and EU) for redundancy, plus an optional third MQTT broker for your own infrastructure:
|
||||
|
||||
Add this to your `/etc/nixos/configuration.nix`:
|
||||
|
||||
```nix
|
||||
{
|
||||
imports = [
|
||||
(builtins.fetchTarball "https://github.com/agessaman/meshcore-packet-capture/archive/main.tar.gz")
|
||||
];
|
||||
|
||||
services.meshcore-packet-capture = {
|
||||
enable = true;
|
||||
connectionType = "ble"; # or "serial" or "tcp"
|
||||
|
||||
# Connection settings (choose one based on connectionType)
|
||||
# For BLE:
|
||||
# bleAddress = "AA:BB:CC:DD:EE:FF"; # optional: specific device address
|
||||
# bleDeviceName = "MeshCore Device"; # optional: device name to scan for
|
||||
|
||||
# For Serial:
|
||||
# serialPorts = [ "/dev/ttyUSB0" "/dev/ttyUSB1" ]; # list of ports to try
|
||||
|
||||
# For TCP:
|
||||
# tcpHost = "localhost"; # TCP server hostname
|
||||
# tcpPort = 5000; # TCP server port
|
||||
|
||||
# Let'sMesh Analyzer - US Server
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = "mqtt-us-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-us-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Let'sMesh Analyzer - EU Server
|
||||
mqtt2 = {
|
||||
enabled = true;
|
||||
server = "mqtt-eu-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-eu-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Optional: Your own MQTT broker (uncomment and configure as needed)
|
||||
# mqtt3 = {
|
||||
# enabled = true;
|
||||
# server = "mqtt.example.com";
|
||||
# port = 1883;
|
||||
# username = "your_username";
|
||||
# password = "your_password";
|
||||
# # or use TLS:
|
||||
# # port = 8883;
|
||||
# # useTLS = true;
|
||||
# };
|
||||
|
||||
# Device private key for Let'sMesh authentication
|
||||
# The script automatically fetches the private key from the device if it supports
|
||||
# ENABLE_PRIVATE_KEY_EXPORT. Only provide these if automatic fetching fails:
|
||||
# privateKeyFile = "/path/to/your/private/key/file";
|
||||
# OR
|
||||
# privateKey = "your_private_key_hex_string";
|
||||
|
||||
# Optional: Owner information for Let'sMesh Analyzer
|
||||
# ownerPublicKey = "YOUR_64_CHAR_HEX_PUBLIC_KEY"; # 64 hex characters
|
||||
# ownerEmail = "your.email@example.com"; # Email for Let'sMesh Analyzer
|
||||
|
||||
# Optional: IATA code for topic templates
|
||||
iata = "SEA"; # Replace with your IATA code
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Then rebuild your system:
|
||||
|
||||
```bash
|
||||
sudo nixos-rebuild switch
|
||||
```
|
||||
|
||||
**Note:** For Let'sMesh Analyzer authentication, you need your device's private key. See the [Authentication](#authentication) section below for details.
|
||||
|
||||
### Custom MQTT Broker Configuration
|
||||
|
||||
If you prefer to use your own MQTT broker instead of (or in addition to) Let'sMesh Analyzer:
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
enable = true;
|
||||
connectionType = "ble";
|
||||
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = "mqtt.example.com";
|
||||
port = 1883; # or 8883 for TLS
|
||||
username = "your_username";
|
||||
password = "your_password";
|
||||
# Optional: Enable TLS
|
||||
# useTLS = true;
|
||||
# tlsVerify = true;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Using with Flakes
|
||||
|
||||
If you're using Nix Flakes, add this to your `flake.nix`:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
meshcore-packet-capture = {
|
||||
url = "github:agessaman/meshcore-packet-capture";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, meshcore-packet-capture }: {
|
||||
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
meshcore-packet-capture.nixosModules.default
|
||||
{
|
||||
services.meshcore-packet-capture = {
|
||||
enable = true;
|
||||
package = meshcore-packet-capture.packages.${system}.default;
|
||||
connectionType = "ble";
|
||||
|
||||
# Let'sMesh Analyzer - US Server
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = "mqtt-us-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-us-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Let'sMesh Analyzer - EU Server
|
||||
mqtt2 = {
|
||||
enabled = true;
|
||||
server = "mqtt-eu-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-eu-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Device private key is automatically fetched from the device
|
||||
# Only set these if automatic fetching fails:
|
||||
# privateKeyFile = "/path/to/your/private/key/file";
|
||||
# privateKey = "your_private_key_hex_string";
|
||||
|
||||
# Optional: Owner information for Let'sMesh Analyzer
|
||||
# ownerPublicKey = "YOUR_64_CHAR_HEX_PUBLIC_KEY";
|
||||
# ownerEmail = "your.email@example.com";
|
||||
|
||||
iata = "SEA";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Connection Settings
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
connectionType = "ble"; # or "serial" or "tcp"
|
||||
bleAddress = "AA:BB:CC:DD:EE:FF"; # optional
|
||||
bleDeviceName = "MeshCore Device"; # optional
|
||||
serialPorts = [ "/dev/ttyUSB0" "/dev/ttyUSB1" ]; # for serial connection
|
||||
tcpHost = "localhost"; # for TCP connection
|
||||
tcpPort = 5000; # for TCP connection
|
||||
timeout = 30;
|
||||
maxConnectionRetries = 5; # 0 = infinite
|
||||
connectionRetryDelay = 5;
|
||||
healthCheckInterval = 30;
|
||||
};
|
||||
```
|
||||
|
||||
### MQTT Brokers
|
||||
|
||||
You can configure up to 4 MQTT brokers. Here's an example with Let'sMesh Analyzer (recommended) plus a custom broker:
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
# Let'sMesh Analyzer - US Server
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = "mqtt-us-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-us-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Let'sMesh Analyzer - EU Server (for redundancy)
|
||||
mqtt2 = {
|
||||
enabled = true;
|
||||
server = "mqtt-eu-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-eu-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Your own MQTT broker (optional)
|
||||
mqtt3 = {
|
||||
enabled = true;
|
||||
server = "mqtt.example.com";
|
||||
port = 1883; # or 8883 for TLS
|
||||
username = "user";
|
||||
password = "pass";
|
||||
transport = "tcp"; # or "websockets"
|
||||
useTLS = false; # set to true for TLS
|
||||
tlsVerify = true;
|
||||
qos = 0;
|
||||
retain = false;
|
||||
keepalive = 60;
|
||||
# Optional topic overrides
|
||||
topicStatus = "meshcore/status";
|
||||
topicPackets = "meshcore/packets";
|
||||
topicRaw = "meshcore/raw";
|
||||
};
|
||||
|
||||
# mqtt4 can be configured similarly
|
||||
};
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
For username/password authentication:
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
mqtt1 = {
|
||||
username = "your_username";
|
||||
password = "your_password";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
For JWT token authentication:
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
mqtt1 = {
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt.example.com";
|
||||
};
|
||||
# Private key is automatically fetched from the device if it supports ENABLE_PRIVATE_KEY_EXPORT
|
||||
# Only provide these if automatic fetching fails:
|
||||
# privateKey = "your_private_key_hex_string";
|
||||
# OR
|
||||
# privateKeyFile = "/path/to/private/key/file";
|
||||
};
|
||||
```
|
||||
|
||||
### Other Settings
|
||||
|
||||
```nix
|
||||
services.meshcore-packet-capture = {
|
||||
logLevel = "INFO"; # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||
verbose = false;
|
||||
debug = false;
|
||||
enableMqtt = true;
|
||||
maxMqttRetries = 5; # 0 = infinite
|
||||
mqttRetryDelay = 5;
|
||||
exitOnReconnectFail = true;
|
||||
iata = "SEA"; # For topic templates
|
||||
origin = "My Device";
|
||||
advertIntervalHours = 11; # 0 = disabled
|
||||
uploadPacketTypes = [ 0 1 2 ]; # Filter packet types, null = all
|
||||
rfDataTimeout = 15.0;
|
||||
outputFile = null; # Optional output file path
|
||||
# privateKeyFile = "/path/to/private/key/file"; # Only if auto-fetch fails
|
||||
# privateKey = "hex_string"; # Only if auto-fetch fails
|
||||
ownerPublicKey = null; # Optional: 64 hex character owner public key
|
||||
ownerEmail = null; # Optional: Owner email for Let'sMesh Analyzer
|
||||
dataDir = "/var/lib/meshcore-packet-capture";
|
||||
user = "meshcore";
|
||||
group = "meshcore";
|
||||
};
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
The service automatically adds the service user to the `bluetooth` and `dialout` groups for BLE and serial port access.
|
||||
|
||||
## Development
|
||||
|
||||
To enter a development shell with all dependencies:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Package not found
|
||||
|
||||
If you get an error about `meshcore` package not being found, you may need to update the hash in `nix/packages.nix`. The first time you build, Nix will tell you the correct hash to use.
|
||||
|
||||
### BLE not working
|
||||
|
||||
Ensure that:
|
||||
1. Bluetooth is enabled: `services.bluetooth.enable = true;`
|
||||
2. The service user has proper permissions (automatically handled)
|
||||
3. Your Bluetooth adapter is properly configured
|
||||
|
||||
### Serial port not accessible
|
||||
|
||||
Ensure that:
|
||||
1. The device exists: `ls -l /dev/ttyUSB0`
|
||||
2. The service user is in the `dialout` group (automatically handled)
|
||||
3. You've specified the correct port in `serialPorts`
|
||||
|
||||
### Service logs
|
||||
|
||||
View service logs with:
|
||||
|
||||
```bash
|
||||
journalctl -u meshcore-packet-capture -f
|
||||
```
|
||||
|
||||
## Building the Package
|
||||
|
||||
To build just the package (without installing as a service):
|
||||
|
||||
```bash
|
||||
nix build
|
||||
```
|
||||
|
||||
The package will be available at `./result/bin/meshcore-packet-capture`.
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# NixOS Testing
|
||||
|
||||
This directory contains testing scripts and documentation for the NixOS service module.
|
||||
|
||||
## Files
|
||||
|
||||
- **`NIXOS.md`** - Complete documentation for using the NixOS service module
|
||||
- **`test-nixos-config.nix`** - Example NixOS configuration for testing (use with `nixos-shell` from repository root)
|
||||
- **`Dockerfile.nix-test`** - Docker image with Nix installed for testing without NixOS
|
||||
- **`docker-test-simple.sh`** - Quick script to get an interactive Docker shell with Nix
|
||||
- **`docker-test.sh`** - Automated test script that runs various Nix flake tests
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Testing in Docker (No NixOS Required)
|
||||
|
||||
Get an interactive shell:
|
||||
```bash
|
||||
cd nixos-test
|
||||
./docker-test-simple.sh
|
||||
```
|
||||
|
||||
Run automated tests:
|
||||
```bash
|
||||
cd nixos-test
|
||||
./docker-test.sh
|
||||
```
|
||||
|
||||
### Testing with nixos-shell (Requires Nix installed)
|
||||
|
||||
From the repository root:
|
||||
```bash
|
||||
nixos-shell nixos-test/test-nixos-config.nix
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See `NIXOS.md` for complete usage instructions and configuration options.
|
||||
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Simple script to get an interactive Docker shell with Nix
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
IMAGE_NAME="meshcore-packet-capture-nix-test"
|
||||
|
||||
# Build the Docker image if it doesn't exist
|
||||
if ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
|
||||
echo "Building Docker image..."
|
||||
docker build -f "$SCRIPT_DIR/Dockerfile.nix-test" -t "$IMAGE_NAME" "$REPO_ROOT"
|
||||
fi
|
||||
|
||||
# Run interactive container
|
||||
echo "Starting interactive container..."
|
||||
echo "The repository is mounted at /workspace"
|
||||
echo "Nix is installed and flakes are enabled"
|
||||
echo ""
|
||||
docker run -it --rm \
|
||||
-v "$REPO_ROOT:/workspace" \
|
||||
-w /workspace \
|
||||
"$IMAGE_NAME" \
|
||||
/bin/bash -c "source /root/.nix-profile/etc/profile.d/nix.sh && /bin/bash"
|
||||
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Script to test the Nix flake in Docker
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
IMAGE_NAME="meshcore-packet-capture-nix-test"
|
||||
CONTAINER_NAME="meshcore-packet-capture-test"
|
||||
|
||||
# Build the Docker image
|
||||
echo "Building Docker image..."
|
||||
docker build -f "$SCRIPT_DIR/Dockerfile.nix-test" -t "$IMAGE_NAME" "$REPO_ROOT"
|
||||
|
||||
# Run the container with the repository mounted
|
||||
echo "Starting container..."
|
||||
docker run -it --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-v "$REPO_ROOT:/workspace" \
|
||||
-w /workspace \
|
||||
"$IMAGE_NAME" \
|
||||
/bin/bash -c "
|
||||
source /root/.nix-profile/etc/profile.d/nix.sh
|
||||
|
||||
echo '=== Testing flake structure ==='
|
||||
nix flake show || true
|
||||
|
||||
echo ''
|
||||
echo '=== Testing package build ==='
|
||||
nix build .#packages.x86_64-linux.default --no-link || true
|
||||
|
||||
echo ''
|
||||
echo '=== Testing module syntax ==='
|
||||
nix-instantiate --eval -E '
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
lib = pkgs.lib;
|
||||
flakeModule = import ./nix/nixos-module.nix { flake-parts-lib = {}; };
|
||||
nixosModule = flakeModule.flake.nixosModules.default;
|
||||
in
|
||||
lib.isFunction nixosModule
|
||||
' --strict || true
|
||||
|
||||
echo ''
|
||||
echo '=== Testing module with minimal config ==='
|
||||
nix-instantiate --eval -E '
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
lib = pkgs.lib;
|
||||
flakeModule = import ./nix/nixos-module.nix { flake-parts-lib = {}; };
|
||||
nixosModule = flakeModule.flake.nixosModules.default;
|
||||
eval = import <nixpkgs/nixos/lib/eval-config.nix> {
|
||||
modules = [
|
||||
nixosModule
|
||||
{
|
||||
services.meshcore-packet-capture = {
|
||||
enable = true;
|
||||
connectionType = \"ble\";
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = \"localhost\";
|
||||
port = 1883;
|
||||
};
|
||||
package = pkgs.hello; # Use a dummy package for testing
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
in
|
||||
eval.config.services.meshcore-packet-capture.enable
|
||||
' --strict || true
|
||||
|
||||
echo ''
|
||||
echo '=== Interactive shell available ==='
|
||||
echo 'Run: docker exec -it $CONTAINER_NAME /bin/bash'
|
||||
/bin/bash
|
||||
"
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
{ config, pkgs, ... }:
|
||||
let
|
||||
# Get the package from the flake using builtins.getFlake
|
||||
# This requires the flake to be accessible (either in registry or as a path)
|
||||
# Note: This file should be run from the repository root, or paths adjusted accordingly
|
||||
repoRoot = toString ../.;
|
||||
flake = builtins.getFlake repoRoot;
|
||||
# Get the package for the current system
|
||||
system = pkgs.system;
|
||||
meshcorePackage = flake.packages.${system}.default;
|
||||
|
||||
# Import the flake module and extract the NixOS module
|
||||
flakeModule = import ../nix/nixos-module.nix { flake-parts-lib = {}; };
|
||||
meshcoreModule = flakeModule.flake.nixosModules.default;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
meshcoreModule
|
||||
];
|
||||
|
||||
# Set system state version to avoid warnings
|
||||
system.stateVersion = "24.11";
|
||||
|
||||
services.meshcore-packet-capture = {
|
||||
enable = true;
|
||||
|
||||
# Override the package to use the flake's package
|
||||
package = meshcorePackage;
|
||||
|
||||
connectionType = "ble";
|
||||
bleDeviceName = "MeshCore-HOWL Ikoka"; # Specific device to connect to
|
||||
# OR use bleAddress = "AA:BB:CC:DD:EE:FF"; for a specific address
|
||||
|
||||
# Let'sMesh Analyzer - US Server
|
||||
mqtt1 = {
|
||||
enabled = true;
|
||||
server = "mqtt-us-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-us-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Let'sMesh Analyzer - EU Server
|
||||
mqtt2 = {
|
||||
enabled = true;
|
||||
server = "mqtt-eu-v1.letsmesh.net";
|
||||
port = 443;
|
||||
transport = "websockets";
|
||||
useTLS = true;
|
||||
useAuthToken = true;
|
||||
tokenAudience = "mqtt-eu-v1.letsmesh.net";
|
||||
keepalive = 120;
|
||||
};
|
||||
|
||||
# Device private key for Let'sMesh authentication
|
||||
# The script will automatically fetch the private key from the device if it supports
|
||||
# ENABLE_PRIVATE_KEY_EXPORT. Only provide these if automatic fetching fails:
|
||||
# privateKeyFile = "/path/to/your/private/key/file";
|
||||
# OR
|
||||
# privateKey = "your_private_key_hex_string";
|
||||
|
||||
# Optional: Owner information for Let'sMesh Analyzer
|
||||
# ownerPublicKey = "dadadadaa3965eb49a7aa8158bd4a4f3a73f711585f8dcffb13a7497e071ddda";
|
||||
# ownerEmail = "adam@gessaman.com";
|
||||
|
||||
# IATA code for topic templates
|
||||
iata = "SEA"; # Replace with your IATA code
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue