mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 03:13:00 +00:00
Renamed debros to orama
This commit is contained in:
parent
ba4e2688e4
commit
29d255676f
4
.github/workflows/release-apt.yml
vendored
4
.github/workflows/release-apt.yml
vendored
@ -57,9 +57,9 @@ jobs:
|
||||
|
||||
mkdir -p build/usr/local/bin
|
||||
go build -ldflags "$LDFLAGS" -o build/usr/local/bin/orama cmd/cli/main.go
|
||||
go build -ldflags "$LDFLAGS" -o build/usr/local/bin/debros-node cmd/node/main.go
|
||||
go build -ldflags "$LDFLAGS" -o build/usr/local/bin/orama-node cmd/node/main.go
|
||||
# Build the entire gateway package so helper files (e.g., config parsing) are included
|
||||
go build -ldflags "$LDFLAGS" -o build/usr/local/bin/debros-gateway ./cmd/gateway
|
||||
go build -ldflags "$LDFLAGS" -o build/usr/local/bin/orama-gateway ./cmd/gateway
|
||||
|
||||
- name: Create Debian package structure
|
||||
run: |
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -104,7 +104,7 @@ website/
|
||||
|
||||
terms-agreement
|
||||
|
||||
cli
|
||||
./cli
|
||||
./inspector
|
||||
|
||||
results/
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# Builds and releases orama (CLI) and orama-node binaries
|
||||
# Publishes to: GitHub Releases, Homebrew, and apt (.deb packages)
|
||||
|
||||
project_name: debros-network
|
||||
project_name: orama-network
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
@ -75,7 +75,7 @@ nfpms:
|
||||
- orama
|
||||
vendor: DeBros
|
||||
homepage: https://github.com/DeBrosOfficial/network
|
||||
maintainer: DeBros <support@debros.io>
|
||||
maintainer: DeBros <support@orama.io>
|
||||
description: CLI tool for the Orama decentralized network
|
||||
license: MIT
|
||||
formats:
|
||||
@ -97,7 +97,7 @@ nfpms:
|
||||
- orama-node
|
||||
vendor: DeBros
|
||||
homepage: https://github.com/DeBrosOfficial/network
|
||||
maintainer: DeBros <support@debros.io>
|
||||
maintainer: DeBros <support@orama.io>
|
||||
description: Node daemon for the Orama decentralized network
|
||||
license: MIT
|
||||
formats:
|
||||
|
||||
@ -32,7 +32,7 @@ This Code applies within all project spaces and when an individual is officially
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the maintainers at: security@debros.io
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the maintainers at: security@orama.io
|
||||
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
|
||||
41
Makefile
41
Makefile
@ -81,47 +81,16 @@ build: deps
|
||||
go build -ldflags "$(LDFLAGS) -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildVersion=$(VERSION)' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildCommit=$(COMMIT)' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildTime=$(DATE)'" -o bin/gateway ./cmd/gateway
|
||||
@echo "Build complete! Run ./bin/orama version"
|
||||
|
||||
# Cross-compile all binaries for Linux (used with --pre-built flag on VPS)
|
||||
# Builds: DeBros binaries + Olric + CoreDNS (with rqlite plugin) + Caddy (with orama DNS module)
|
||||
# Cross-compile CLI for Linux (only binary needed locally; VPS builds everything else from source)
|
||||
build-linux: deps
|
||||
@echo "Cross-compiling all binaries for linux/amd64 (version=$(VERSION))..."
|
||||
@echo "Cross-compiling CLI for linux/amd64 (version=$(VERSION))..."
|
||||
@mkdir -p bin-linux
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/identity ./cmd/identity
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama-node ./cmd/node
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama cmd/cli/main.go
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/rqlite-mcp ./cmd/rqlite-mcp
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX) -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildVersion=$(VERSION)' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildCommit=$(COMMIT)' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildTime=$(DATE)'" -trimpath -o bin-linux/gateway ./cmd/gateway
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama-cli ./cmd/cli
|
||||
@echo "Building Olric for linux/amd64..."
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -trimpath -o bin-linux/olric-server github.com/olric-data/olric/cmd/olric-server
|
||||
@echo "Building IPFS Cluster Service for linux/amd64..."
|
||||
GOOS=linux GOARCH=amd64 GOBIN=$(CURDIR)/bin-linux go install -ldflags "-s -w" -trimpath github.com/ipfs-cluster/ipfs-cluster/cmd/ipfs-cluster-service@latest
|
||||
@echo "✓ All Linux binaries built in bin-linux/"
|
||||
@echo "✓ CLI built at bin-linux/orama"
|
||||
@echo ""
|
||||
@echo "Next steps:"
|
||||
@echo " 1. Build CoreDNS: make build-linux-coredns"
|
||||
@echo " 2. Build Caddy: make build-linux-caddy"
|
||||
@echo " 3. Or build all: make build-linux-all"
|
||||
|
||||
# Build CoreDNS with rqlite plugin for Linux
|
||||
build-linux-coredns:
|
||||
@bash scripts/build-linux-coredns.sh
|
||||
|
||||
# Build Caddy with orama DNS module for Linux
|
||||
build-linux-caddy:
|
||||
@bash scripts/build-linux-caddy.sh
|
||||
|
||||
# Build everything for Linux (all binaries + CoreDNS + Caddy)
|
||||
build-linux-all: build-linux build-linux-coredns build-linux-caddy
|
||||
@echo ""
|
||||
@echo "✅ All Linux binaries ready in bin-linux/:"
|
||||
@ls -la bin-linux/
|
||||
@echo ""
|
||||
@echo "Deploy to VPS:"
|
||||
@echo " scp bin-linux/* ubuntu@<ip>:/home/debros/bin/"
|
||||
@echo " scp bin-linux/coredns ubuntu@<ip>:/usr/local/bin/coredns"
|
||||
@echo " scp bin-linux/caddy ubuntu@<ip>:/usr/bin/caddy"
|
||||
@echo " sudo orama install --pre-built --no-pull ..."
|
||||
@echo " ./scripts/generate-source-archive.sh"
|
||||
@echo " ./bin/orama install --vps-ip <ip> --nameserver --domain ..."
|
||||
|
||||
# Install git hooks
|
||||
install-hooks:
|
||||
|
||||
@ -360,13 +360,13 @@ All configuration lives in `~/.orama/`:
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
systemctl status debros-node
|
||||
systemctl status orama-node
|
||||
|
||||
# View logs
|
||||
journalctl -u debros-node -f
|
||||
journalctl -u orama-node -f
|
||||
|
||||
# Check log files
|
||||
tail -f /home/debros/.orama/logs/node.log
|
||||
tail -f /home/orama/.orama/logs/node.log
|
||||
```
|
||||
|
||||
### Port Conflicts
|
||||
@ -398,7 +398,7 @@ rqlite -H localhost -p 5001
|
||||
```bash
|
||||
# Production reset (⚠️ DESTROYS DATA)
|
||||
sudo orama uninstall
|
||||
sudo rm -rf /home/debros/.orama
|
||||
sudo rm -rf /home/orama/.orama
|
||||
sudo orama install
|
||||
```
|
||||
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@ -4,7 +4,7 @@ Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Depends: libc6
|
||||
Maintainer: DeBros Team <dev@debros.io>
|
||||
Maintainer: DeBros Team <dev@orama.io>
|
||||
Description: Orama Network - Distributed P2P Database System
|
||||
Orama is a distributed peer-to-peer network that combines
|
||||
RQLite for distributed SQL, IPFS for content-addressed storage,
|
||||
|
||||
@ -8,11 +8,11 @@ Run this as root or with sudo on the target VPS:
|
||||
|
||||
```bash
|
||||
# 1. Stop and disable all services
|
||||
sudo systemctl stop debros-node debros-ipfs debros-ipfs-cluster debros-olric debros-anyone-relay debros-anyone-client coredns caddy 2>/dev/null
|
||||
sudo systemctl disable debros-node debros-ipfs debros-ipfs-cluster debros-olric debros-anyone-relay debros-anyone-client coredns caddy 2>/dev/null
|
||||
sudo systemctl stop orama-node orama-ipfs orama-ipfs-cluster orama-olric orama-anyone-relay orama-anyone-client coredns caddy 2>/dev/null
|
||||
sudo systemctl disable orama-node orama-ipfs orama-ipfs-cluster orama-olric orama-anyone-relay orama-anyone-client coredns caddy 2>/dev/null
|
||||
|
||||
# 2. Remove systemd service files
|
||||
sudo rm -f /etc/systemd/system/debros-*.service
|
||||
sudo rm -f /etc/systemd/system/orama-*.service
|
||||
sudo rm -f /etc/systemd/system/coredns.service
|
||||
sudo rm -f /etc/systemd/system/caddy.service
|
||||
sudo systemctl daemon-reload
|
||||
@ -31,14 +31,14 @@ sudo ufw --force reset
|
||||
sudo ufw allow 22/tcp
|
||||
sudo ufw --force enable
|
||||
|
||||
# 5. Remove debros user and home directory
|
||||
sudo userdel -r debros 2>/dev/null
|
||||
sudo rm -rf /home/debros
|
||||
# 5. Remove orama user and home directory
|
||||
sudo userdel -r orama 2>/dev/null
|
||||
sudo rm -rf /home/orama
|
||||
|
||||
# 6. Remove sudoers files
|
||||
sudo rm -f /etc/sudoers.d/debros-access
|
||||
sudo rm -f /etc/sudoers.d/debros-deployments
|
||||
sudo rm -f /etc/sudoers.d/debros-wireguard
|
||||
sudo rm -f /etc/sudoers.d/orama-access
|
||||
sudo rm -f /etc/sudoers.d/orama-deployments
|
||||
sudo rm -f /etc/sudoers.d/orama-wireguard
|
||||
|
||||
# 7. Remove CoreDNS config
|
||||
sudo rm -rf /etc/coredns
|
||||
@ -62,17 +62,17 @@ echo "Node cleaned. Ready for fresh install."
|
||||
|
||||
| Category | Paths |
|
||||
|----------|-------|
|
||||
| **User** | `debros` system user and `/home/debros/` |
|
||||
| **App data** | `/home/debros/.orama/` (configs, secrets, logs, IPFS, RQLite, Olric) |
|
||||
| **Source code** | `/home/debros/src/` |
|
||||
| **Binaries** | `/home/debros/bin/orama-node`, `/home/debros/bin/gateway` |
|
||||
| **Systemd** | `debros-*.service`, `coredns.service`, `caddy.service`, `orama-deploy-*.service` |
|
||||
| **User** | `orama` system user and `/home/orama/` |
|
||||
| **App data** | `/home/orama/.orama/` (configs, secrets, logs, IPFS, RQLite, Olric) |
|
||||
| **Source code** | `/home/orama/src/` |
|
||||
| **Binaries** | `/home/orama/bin/orama-node`, `/home/orama/bin/gateway` |
|
||||
| **Systemd** | `orama-*.service`, `coredns.service`, `caddy.service`, `orama-deploy-*.service` |
|
||||
| **WireGuard** | `/etc/wireguard/wg0.conf`, `wg-quick@wg0` systemd unit |
|
||||
| **Firewall** | All UFW rules (reset to default + SSH only) |
|
||||
| **Sudoers** | `/etc/sudoers.d/debros-*` |
|
||||
| **Sudoers** | `/etc/sudoers.d/orama-*` |
|
||||
| **CoreDNS** | `/etc/coredns/Corefile` |
|
||||
| **Caddy** | `/etc/caddy/Caddyfile`, `/var/lib/caddy/` (TLS certs) |
|
||||
| **Anyone Relay** | `debros-anyone-relay.service`, `debros-anyone-client.service` |
|
||||
| **Anyone Relay** | `orama-anyone-relay.service`, `orama-anyone-client.service` |
|
||||
| **Temp files** | `/tmp/orama`, `/tmp/network-source.*`, build dirs |
|
||||
|
||||
## What This Does NOT Remove
|
||||
@ -121,18 +121,18 @@ for entry in "${NODES[@]}"; do
|
||||
IFS=: read -r userhost pass <<< "$entry"
|
||||
echo "Cleaning $userhost..."
|
||||
sshpass -p "$pass" ssh -o StrictHostKeyChecking=no "$userhost" 'bash -s' << 'CLEAN'
|
||||
sudo systemctl stop debros-node debros-ipfs debros-ipfs-cluster debros-olric debros-anyone-relay debros-anyone-client coredns caddy 2>/dev/null
|
||||
sudo systemctl disable debros-node debros-ipfs debros-ipfs-cluster debros-olric debros-anyone-relay debros-anyone-client coredns caddy 2>/dev/null
|
||||
sudo rm -f /etc/systemd/system/debros-*.service /etc/systemd/system/coredns.service /etc/systemd/system/caddy.service /etc/systemd/system/orama-deploy-*.service
|
||||
sudo systemctl stop orama-node orama-ipfs orama-ipfs-cluster orama-olric orama-anyone-relay orama-anyone-client coredns caddy 2>/dev/null
|
||||
sudo systemctl disable orama-node orama-ipfs orama-ipfs-cluster orama-olric orama-anyone-relay orama-anyone-client coredns caddy 2>/dev/null
|
||||
sudo rm -f /etc/systemd/system/orama-*.service /etc/systemd/system/coredns.service /etc/systemd/system/caddy.service /etc/systemd/system/orama-deploy-*.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl stop wg-quick@wg0 2>/dev/null
|
||||
sudo wg-quick down wg0 2>/dev/null
|
||||
sudo systemctl disable wg-quick@wg0 2>/dev/null
|
||||
sudo rm -f /etc/wireguard/wg0.conf
|
||||
sudo ufw --force reset && sudo ufw allow 22/tcp && sudo ufw --force enable
|
||||
sudo userdel -r debros 2>/dev/null
|
||||
sudo rm -rf /home/debros
|
||||
sudo rm -f /etc/sudoers.d/debros-access /etc/sudoers.d/debros-deployments /etc/sudoers.d/debros-wireguard
|
||||
sudo userdel -r orama 2>/dev/null
|
||||
sudo rm -rf /home/orama
|
||||
sudo rm -f /etc/sudoers.d/orama-access /etc/sudoers.d/orama-deployments /etc/sudoers.d/orama-wireguard
|
||||
sudo rm -rf /etc/coredns /etc/caddy /var/lib/caddy
|
||||
sudo rm -f /tmp/orama /tmp/network-source.tar.gz
|
||||
sudo rm -rf /tmp/network-extract /tmp/coredns-build /tmp/caddy-build
|
||||
|
||||
@ -41,7 +41,7 @@ You can find peer public keys with `wg show wg0`.
|
||||
Check the Olric config on each node:
|
||||
|
||||
```bash
|
||||
cat /home/debros/.orama/data/namespaces/<name>/configs/olric-*.yaml
|
||||
cat /home/orama/.orama/data/namespaces/<name>/configs/olric-*.yaml
|
||||
```
|
||||
|
||||
If `bindAddr` is `0.0.0.0`, the node will try to bind to IPv6 on dual-stack hosts, breaking memberlist gossip.
|
||||
@ -53,7 +53,7 @@ This was fixed in code (BindAddr validation in `SpawnOlric`), so new namespaces
|
||||
### Check 3: Olric logs show "Failed UDP ping" constantly
|
||||
|
||||
```bash
|
||||
journalctl -u debros-namespace-olric@<name>.service --no-pager -n 30
|
||||
journalctl -u orama-namespace-olric@<name>.service --no-pager -n 30
|
||||
```
|
||||
|
||||
If every UDP ping fails but TCP stream connections succeed, it's the WireGuard packet loss issue (see Check 1).
|
||||
@ -69,7 +69,7 @@ If every UDP ping fails but TCP stream connections succeed, it's the WireGuard p
|
||||
**Fix:** Edit the gateway config manually:
|
||||
|
||||
```bash
|
||||
vim /home/debros/.orama/data/namespaces/<name>/configs/gateway-*.yaml
|
||||
vim /home/orama/.orama/data/namespaces/<name>/configs/gateway-*.yaml
|
||||
```
|
||||
|
||||
Add/fix:
|
||||
@ -95,7 +95,7 @@ This was fixed in code, so new namespaces get the correct config.
|
||||
**Check:**
|
||||
|
||||
```bash
|
||||
ls /home/debros/.orama/data/namespaces/<name>/cluster-state.json
|
||||
ls /home/orama/.orama/data/namespaces/<name>/cluster-state.json
|
||||
```
|
||||
|
||||
If the file doesn't exist, the node can't restore the namespace.
|
||||
@ -119,14 +119,14 @@ This was fixed in code — `ProvisionCluster` now saves state to all nodes (incl
|
||||
|
||||
**Symptom:** After `orama upgrade --restart` or `orama prod restart`, namespace gateway/olric/rqlite services don't start.
|
||||
|
||||
**Cause:** `orama prod stop` disables systemd template services (`debros-namespace-gateway@<name>.service`). They have `PartOf=debros-node.service`, but that only propagates restart to **enabled** services.
|
||||
**Cause:** `orama prod stop` disables systemd template services (`orama-namespace-gateway@<name>.service`). They have `PartOf=orama-node.service`, but that only propagates restart to **enabled** services.
|
||||
|
||||
**Fix:** Re-enable the services before restarting:
|
||||
|
||||
```bash
|
||||
systemctl enable debros-namespace-rqlite@<name>.service
|
||||
systemctl enable debros-namespace-olric@<name>.service
|
||||
systemctl enable debros-namespace-gateway@<name>.service
|
||||
systemctl enable orama-namespace-rqlite@<name>.service
|
||||
systemctl enable orama-namespace-olric@<name>.service
|
||||
systemctl enable orama-namespace-gateway@<name>.service
|
||||
sudo orama prod restart
|
||||
```
|
||||
|
||||
@ -153,8 +153,8 @@ ssh -n user@host 'command'
|
||||
## General Debugging Tips
|
||||
|
||||
- **Always use `sudo orama prod restart`** instead of raw `systemctl` commands
|
||||
- **Namespace data lives at:** `/home/debros/.orama/data/namespaces/<name>/`
|
||||
- **Check service logs:** `journalctl -u debros-namespace-olric@<name>.service --no-pager -n 50`
|
||||
- **Namespace data lives at:** `/home/orama/.orama/data/namespaces/<name>/`
|
||||
- **Check service logs:** `journalctl -u orama-namespace-olric@<name>.service --no-pager -n 50`
|
||||
- **Check WireGuard:** `wg show wg0` — look for recent handshakes and transfer bytes
|
||||
- **Check gateway health:** `curl http://localhost:<port>/v1/health` from the node itself
|
||||
- **Node IPs:** Check `scripts/remote-nodes.conf` for credentials, `wg show wg0` for WG IPs
|
||||
|
||||
@ -369,7 +369,7 @@ orama db create my-database
|
||||
# Output:
|
||||
# ✅ Database created: my-database
|
||||
# Home Node: node-abc123
|
||||
# File Path: /home/debros/.orama/data/sqlite/your-namespace/my-database.db
|
||||
# File Path: /home/orama/.orama/data/sqlite/your-namespace/my-database.db
|
||||
```
|
||||
|
||||
### Executing Queries
|
||||
@ -588,7 +588,7 @@ func main() {
|
||||
// DATABASE_NAME env var is automatically set by Orama
|
||||
dbPath := os.Getenv("DATABASE_PATH")
|
||||
if dbPath == "" {
|
||||
dbPath = "/home/debros/.orama/data/sqlite/" + os.Getenv("NAMESPACE") + "/myapp-db.db"
|
||||
dbPath = "/home/orama/.orama/data/sqlite/" + os.Getenv("NAMESPACE") + "/myapp-db.db"
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
@ -41,7 +41,7 @@ Install nodes **one at a time**, waiting for each to complete before starting th
|
||||
```bash
|
||||
# SSH: <user>@<ns1-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--vps-ip <ns1-ip> \
|
||||
--domain <your-domain.com> \
|
||||
--base-domain <your-domain.com> \
|
||||
@ -58,7 +58,7 @@ orama invite --expiry 24h
|
||||
```bash
|
||||
# SSH: <user>@<ns2-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--join http://<ns1-ip> --token <TOKEN> \
|
||||
--vps-ip <ns2-ip> \
|
||||
--domain <your-domain.com> \
|
||||
@ -77,7 +77,7 @@ sudo orama install --no-pull --pre-built \
|
||||
```bash
|
||||
# SSH: <user>@<ns3-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--join http://<ns1-ip> --token <TOKEN> \
|
||||
--vps-ip <ns3-ip> \
|
||||
--domain <your-domain.com> \
|
||||
@ -96,7 +96,7 @@ sudo orama install --no-pull --pre-built \
|
||||
```bash
|
||||
# SSH: <user>@<node4-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--join http://<ns1-ip> --token <TOKEN> \
|
||||
--vps-ip <node4-ip> \
|
||||
--domain node4.<your-domain.com> \
|
||||
@ -115,7 +115,7 @@ sudo orama install --no-pull --pre-built \
|
||||
```bash
|
||||
# SSH: <user>@<node5-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--join http://<ns1-ip> --token <TOKEN> \
|
||||
--vps-ip <node5-ip> \
|
||||
--domain node5.<your-domain.com> \
|
||||
@ -134,7 +134,7 @@ sudo orama install --no-pull --pre-built \
|
||||
```bash
|
||||
# SSH: <user>@<node6-ip>
|
||||
|
||||
sudo orama install --no-pull --pre-built \
|
||||
sudo orama install \
|
||||
--join http://<ns1-ip> --token <TOKEN> \
|
||||
--vps-ip <node6-ip> \
|
||||
--domain node6.<your-domain.com> \
|
||||
@ -155,5 +155,5 @@ curl -s http://localhost:5001/status | jq -r '.store.raft.state, .store.raft.num
|
||||
curl -s http://localhost:6001/health
|
||||
|
||||
# Check Anyone relay (on nodes with relays)
|
||||
systemctl status debros-anyone-relay
|
||||
systemctl status orama-anyone-relay
|
||||
```
|
||||
|
||||
@ -28,83 +28,29 @@ make test
|
||||
|
||||
## Deploying to VPS
|
||||
|
||||
There are two deployment workflows: **development** (fast iteration, no git required) and **production** (via git).
|
||||
Source is always deployed via SCP (no git on VPS). The CLI is the only binary cross-compiled locally; everything else is built from source on the VPS.
|
||||
|
||||
### Development Deployment (Fast Iteration)
|
||||
|
||||
Use this when iterating quickly — no need to commit or push to git.
|
||||
### Deploy Workflow
|
||||
|
||||
```bash
|
||||
# 1. Build the CLI for Linux
|
||||
GOOS=linux GOARCH=amd64 go build -o orama-cli-linux ./cmd/cli
|
||||
# 1. Cross-compile the CLI for Linux
|
||||
make build-linux
|
||||
|
||||
# 2. Generate a source archive (excludes .git, node_modules, bin/, etc.)
|
||||
# 2. Generate a source archive (includes CLI binary + full source)
|
||||
./scripts/generate-source-archive.sh
|
||||
# Creates: /tmp/network-source.tar.gz
|
||||
|
||||
# 3. Copy CLI and source to the VPS
|
||||
sshpass -p '<password>' scp -o StrictHostKeyChecking=no orama-cli-linux ubuntu@<ip>:/tmp/orama
|
||||
sshpass -p '<password>' scp -o StrictHostKeyChecking=no /tmp/network-source.tar.gz ubuntu@<ip>:/tmp/
|
||||
# 3. Install on a new VPS (handles SCP, extract, and remote install automatically)
|
||||
./bin/orama install --vps-ip <ip> --nameserver --domain <domain> --base-domain <domain>
|
||||
|
||||
# 4. On the VPS: extract source and install the CLI
|
||||
ssh ubuntu@<ip>
|
||||
sudo rm -rf /home/debros/src && sudo mkdir -p /home/debros/src
|
||||
sudo tar xzf /tmp/network-source.tar.gz -C /home/debros/src
|
||||
sudo chown -R debros:debros /home/debros/src
|
||||
sudo mv /tmp/orama /usr/local/bin/orama && sudo chmod +x /usr/local/bin/orama
|
||||
|
||||
# 5. Upgrade using local source (skips git pull)
|
||||
sudo orama upgrade --no-pull --restart
|
||||
# Or upgrade an existing VPS
|
||||
./bin/orama upgrade --restart
|
||||
```
|
||||
|
||||
### Development Deployment with Pre-Built Binaries (Fastest)
|
||||
|
||||
Cross-compile everything locally and skip all Go compilation on the VPS. This is significantly faster because your local machine compiles much faster than the VPS.
|
||||
|
||||
```bash
|
||||
# 1. Cross-compile all binaries for Linux (DeBros + Olric + CoreDNS + Caddy)
|
||||
make build-linux-all
|
||||
# Outputs everything to bin-linux/
|
||||
|
||||
# 2. Generate a single deploy archive (source + pre-built binaries)
|
||||
./scripts/generate-source-archive.sh
|
||||
# Creates: /tmp/network-source.tar.gz (includes bin-linux/ if present)
|
||||
|
||||
# 3. Copy the single archive to the VPS
|
||||
sshpass -p '<password>' scp -o StrictHostKeyChecking=no /tmp/network-source.tar.gz ubuntu@<ip>:/tmp/
|
||||
|
||||
# 4. Extract and install everything on the VPS
|
||||
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no ubuntu@<ip> \
|
||||
'sudo bash -s' < scripts/extract-deploy.sh
|
||||
|
||||
# 5. Install/upgrade with --pre-built (skips ALL Go compilation on VPS)
|
||||
sudo orama install --no-pull --pre-built --vps-ip <ip> ...
|
||||
# or
|
||||
sudo orama upgrade --no-pull --pre-built --restart
|
||||
```
|
||||
|
||||
**What `--pre-built` skips:** Go installation, `make build`, Olric `go install`, CoreDNS build, Caddy/xcaddy build.
|
||||
|
||||
**What `--pre-built` still runs:** apt dependencies, RQLite/IPFS/IPFS Cluster downloads (pre-built binary downloads, fast), Anyone relay setup, config generation, systemd service creation.
|
||||
|
||||
### Production Deployment (Via Git)
|
||||
|
||||
For production releases — pulls source from GitHub on the VPS.
|
||||
|
||||
```bash
|
||||
# 1. Commit and push your changes
|
||||
git push origin <branch>
|
||||
|
||||
# 2. Build the CLI for Linux
|
||||
GOOS=linux GOARCH=amd64 go build -o orama-cli-linux ./cmd/cli
|
||||
|
||||
# 3. Deploy the CLI to the VPS
|
||||
sshpass -p '<password>' scp orama-cli-linux ubuntu@<ip>:/tmp/orama
|
||||
ssh ubuntu@<ip> "sudo mv /tmp/orama /usr/local/bin/orama && sudo chmod +x /usr/local/bin/orama"
|
||||
|
||||
# 4. Run upgrade (downloads source from GitHub)
|
||||
ssh ubuntu@<ip> "sudo orama upgrade --branch <branch> --restart"
|
||||
```
|
||||
The `orama install` command automatically:
|
||||
1. Uploads the source archive via SCP
|
||||
2. Extracts source to `/home/orama/src` and installs the CLI to `/usr/local/bin/orama`
|
||||
3. Runs `orama install` on the VPS which builds all binaries from source (Go, CoreDNS, Caddy, Olric, etc.)
|
||||
|
||||
### Upgrading a Multi-Node Cluster (CRITICAL)
|
||||
|
||||
@ -115,10 +61,10 @@ ssh ubuntu@<ip> "sudo orama upgrade --branch <branch> --restart"
|
||||
Always upgrade nodes **one at a time**, waiting for each to rejoin before proceeding:
|
||||
|
||||
```bash
|
||||
# 1. Build locally
|
||||
make build-linux-all
|
||||
# 1. Build CLI + generate archive
|
||||
make build-linux
|
||||
./scripts/generate-source-archive.sh
|
||||
# Creates: /tmp/network-source.tar.gz (includes bin-linux/)
|
||||
# Creates: /tmp/network-source.tar.gz
|
||||
|
||||
# 2. Upload to ONE node first (the "hub" node)
|
||||
sshpass -p '<password>' scp /tmp/network-source.tar.gz ubuntu@<hub-ip>:/tmp/
|
||||
@ -139,8 +85,7 @@ done
|
||||
ssh ubuntu@<any-node> 'curl -s http://localhost:5001/status | jq -r .store.raft.state'
|
||||
|
||||
# 6. Upgrade FOLLOWER nodes one at a time
|
||||
# First stop services, then upgrade, which restarts them
|
||||
ssh ubuntu@<follower-ip> 'sudo orama prod stop && sudo orama upgrade --no-pull --pre-built --restart'
|
||||
ssh ubuntu@<follower-ip> 'sudo orama prod stop && sudo orama upgrade --restart'
|
||||
|
||||
# Wait for rejoin before proceeding to next node
|
||||
ssh ubuntu@<leader-ip> 'curl -s http://localhost:5001/status | jq -r .store.raft.num_peers'
|
||||
@ -149,7 +94,7 @@ ssh ubuntu@<leader-ip> 'curl -s http://localhost:5001/status | jq -r .store.raft
|
||||
# Repeat for each follower...
|
||||
|
||||
# 7. Upgrade the LEADER node last
|
||||
ssh ubuntu@<leader-ip> 'sudo orama prod stop && sudo orama upgrade --no-pull --pre-built --restart'
|
||||
ssh ubuntu@<leader-ip> 'sudo orama prod stop && sudo orama upgrade --restart'
|
||||
```
|
||||
|
||||
#### What NOT to Do
|
||||
@ -157,7 +102,7 @@ ssh ubuntu@<leader-ip> 'sudo orama prod stop && sudo orama upgrade --no-pull --p
|
||||
- **DON'T** stop all nodes, replace binaries, then start all nodes
|
||||
- **DON'T** run `orama upgrade --restart` on multiple nodes in parallel
|
||||
- **DON'T** clear RQLite data directories unless doing a full cluster rebuild
|
||||
- **DON'T** use `systemctl stop debros-node` on multiple nodes simultaneously
|
||||
- **DON'T** use `systemctl stop orama-node` on multiple nodes simultaneously
|
||||
|
||||
#### Recovery from Cluster Split
|
||||
|
||||
@ -168,8 +113,8 @@ If nodes get stuck in "Candidate" state or show "leader not found" errors:
|
||||
3. On each other node, clear RQLite data and restart:
|
||||
```bash
|
||||
sudo orama prod stop
|
||||
sudo rm -rf /home/debros/.orama/data/rqlite
|
||||
sudo systemctl start debros-node
|
||||
sudo rm -rf /home/orama/.orama/data/rqlite
|
||||
sudo systemctl start orama-node
|
||||
```
|
||||
4. The node should automatically rejoin using its configured `rqlite_join_address`
|
||||
|
||||
@ -180,7 +125,7 @@ ps aux | grep rqlited
|
||||
```
|
||||
|
||||
If `-join` is missing, the node bootstrapped standalone. You'll need to either:
|
||||
- Restart debros-node (it should detect empty data and use join)
|
||||
- Restart orama-node (it should detect empty data and use join)
|
||||
- Or do a full cluster rebuild from CLEAN_NODE.md
|
||||
|
||||
### Deploying to Multiple Nodes
|
||||
@ -201,9 +146,6 @@ To deploy to all nodes, repeat steps 3-5 (dev) or 3-4 (production) for each VPS
|
||||
| `--nameserver` | Configure this node as a nameserver (CoreDNS + Caddy) |
|
||||
| `--join <url>` | Join existing cluster via HTTPS URL (e.g., `https://node1.example.com`) |
|
||||
| `--token <token>` | Invite token for joining (from `orama invite` on existing node) |
|
||||
| `--branch <branch>` | Git branch to use (default: main) |
|
||||
| `--no-pull` | Skip git clone/pull, use existing `/home/debros/src` |
|
||||
| `--pre-built` | Skip all Go compilation, use pre-built binaries already on disk (see above) |
|
||||
| `--force` | Force reconfiguration even if already installed |
|
||||
| `--skip-firewall` | Skip UFW firewall setup |
|
||||
| `--skip-checks` | Skip minimum resource checks (RAM/CPU) |
|
||||
@ -234,9 +176,6 @@ To deploy to all nodes, repeat steps 3-5 (dev) or 3-4 (production) for each VPS
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--branch <branch>` | Git branch to pull from |
|
||||
| `--no-pull` | Skip git pull, use existing source |
|
||||
| `--pre-built` | Skip all Go compilation, use pre-built binaries already on disk |
|
||||
| `--restart` | Restart all services after upgrade |
|
||||
| `--anyone-relay` | Enable Anyone relay (same flags as install) |
|
||||
| `--anyone-bandwidth <pct>` | Limit relay to N% of VPS bandwidth (default: 30, 0=unlimited) |
|
||||
@ -247,7 +186,7 @@ To deploy to all nodes, repeat steps 3-5 (dev) or 3-4 (production) for each VPS
|
||||
Use these commands to manage services on production nodes:
|
||||
|
||||
```bash
|
||||
# Stop all services (debros-node, coredns, caddy)
|
||||
# Stop all services (orama-node, coredns, caddy)
|
||||
sudo orama prod stop
|
||||
|
||||
# Start all services
|
||||
@ -318,12 +257,7 @@ Before running `orama install` on a VPS, ensure:
|
||||
sudo systemctl stop ipfs
|
||||
```
|
||||
|
||||
3. **Ensure `make` is installed.** Required for building CoreDNS and Caddy from source:
|
||||
```bash
|
||||
sudo apt-get install -y make
|
||||
```
|
||||
|
||||
4. **Stop any service on port 53** (for nameserver nodes). The installer handles `systemd-resolved` automatically, but other DNS services (like `bind9` or `dnsmasq`) must be stopped manually.
|
||||
3. **Stop any service on port 53** (for nameserver nodes). The installer handles `systemd-resolved` automatically, but other DNS services (like `bind9` or `dnsmasq`) must be stopped manually.
|
||||
|
||||
## Recovering from Failed Joins
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ Multiple subsystems can be combined: `--subsystem rqlite,olric,dns`
|
||||
| **ipfs** | Daemon active, cluster active, swarm peer count, cluster peer count, cluster errors, repo usage %, swarm key present, bootstrap list empty, cross-node version consistency |
|
||||
| **dns** | CoreDNS active, Caddy active, ports (53/80/443), memory, restart count, log errors, Corefile exists, SOA/NS/wildcard/base-A resolution, TLS cert expiry, cross-node nameserver availability |
|
||||
| **wireguard** | Interface up, service active, correct 10.0.0.x IP, listen port 51820, peer count vs expected, MTU 1420, config exists + permissions 600, peer handshakes (fresh/stale/never), peer traffic, catch-all route detection, cross-node peer count + MTU consistency |
|
||||
| **system** | Core services (debros-node, rqlite, olric, ipfs, ipfs-cluster, wg-quick), nameserver services (coredns, caddy), failed systemd units, memory/disk/inode usage, load average, OOM kills, swap, UFW active, process user (debros), panic count, expected ports |
|
||||
| **system** | Core services (orama-node, rqlite, olric, ipfs, ipfs-cluster, wg-quick), nameserver services (coredns, caddy), failed systemd units, memory/disk/inode usage, load average, OOM kills, swap, UFW active, process user (orama), panic count, expected ports |
|
||||
| **network** | Internet reachability, default route, WireGuard route, TCP connection count, TIME_WAIT count, TCP retransmission rate, WireGuard mesh ping (all peers) |
|
||||
| **namespace** | Per-namespace: RQLite up + raft state + readyz, Olric memberlist, Gateway HTTP health. Cross-namespace: all-healthy check, RQLite quorum per namespace |
|
||||
|
||||
|
||||
@ -297,7 +297,7 @@ func GetRQLiteNodes() []string {
|
||||
// queryAPIKeyFromRQLite queries the SQLite database directly for an API key
|
||||
func queryAPIKeyFromRQLite() (string, error) {
|
||||
// 1. Check environment variable first
|
||||
if envKey := os.Getenv("DEBROS_API_KEY"); envKey != "" {
|
||||
if envKey := os.Getenv("ORAMA_API_KEY"); envKey != "" {
|
||||
return envKey, nil
|
||||
}
|
||||
|
||||
@ -424,7 +424,7 @@ func GetAPIKey() string {
|
||||
cacheMutex.RUnlock()
|
||||
|
||||
// 1. Check env var
|
||||
if envKey := os.Getenv("DEBROS_API_KEY"); envKey != "" {
|
||||
if envKey := os.Getenv("ORAMA_API_KEY"); envKey != "" {
|
||||
cacheMutex.Lock()
|
||||
apiKeyCache = envKey
|
||||
cacheMutex.Unlock()
|
||||
@ -1188,8 +1188,8 @@ type E2ETestEnv struct {
|
||||
GatewayURL string
|
||||
APIKey string
|
||||
Namespace string
|
||||
BaseDomain string // Domain for deployment routing (e.g., "dbrs.space")
|
||||
Config *E2EConfig // Full E2E configuration (for production tests)
|
||||
BaseDomain string // Domain for deployment routing (e.g., "dbrs.space")
|
||||
Config *E2EConfig // Full E2E configuration (for production tests)
|
||||
HTTPClient *http.Client
|
||||
SkipCleanup bool
|
||||
}
|
||||
|
||||
@ -57,16 +57,16 @@ func TestDomainRouting_BasicRouting(t *testing.T) {
|
||||
t.Logf("✓ Standard domain routing works: %s", domain)
|
||||
})
|
||||
|
||||
t.Run("Non-debros domain passes through", func(t *testing.T) {
|
||||
// Request with non-debros domain should not route to deployment
|
||||
t.Run("Non-orama domain passes through", func(t *testing.T) {
|
||||
// Request with non-orama domain should not route to deployment
|
||||
resp := e2e.TestDeploymentWithHostHeader(t, env, "example.com", "/")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Should either return 404 or pass to default handler
|
||||
assert.NotEqual(t, http.StatusOK, resp.StatusCode,
|
||||
"Non-debros domain should not route to deployment")
|
||||
"Non-orama domain should not route to deployment")
|
||||
|
||||
t.Logf("✓ Non-debros domains correctly pass through (status: %d)", resp.StatusCode)
|
||||
t.Logf("✓ Non-orama domains correctly pass through (status: %d)", resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("API paths bypass domain routing", func(t *testing.T) {
|
||||
|
||||
@ -118,7 +118,7 @@ func TestNetwork_ProxyAnonSuccess(t *testing.T) {
|
||||
Body: map[string]interface{}{
|
||||
"url": "https://httpbin.org/get",
|
||||
"method": "GET",
|
||||
"headers": map[string]string{"User-Agent": "DeBros-E2E-Test/1.0"},
|
||||
"headers": map[string]string{"User-Agent": "Orama-E2E-Test/1.0"},
|
||||
},
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ func TestNetwork_ProxyAnonPostRequest(t *testing.T) {
|
||||
Body: map[string]interface{}{
|
||||
"url": "https://httpbin.org/post",
|
||||
"method": "POST",
|
||||
"headers": map[string]string{"User-Agent": "DeBros-E2E-Test/1.0"},
|
||||
"headers": map[string]string{"User-Agent": "Orama-E2E-Test/1.0"},
|
||||
"body": "test_data",
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
-- DeBros Gateway - Initial database schema (SQLite/RQLite dialect)
|
||||
-- Orama Gateway - Initial database schema (SQLite/RQLite dialect)
|
||||
-- This file scaffolds core tables used by the HTTP gateway for auth, observability, and namespacing.
|
||||
-- Apply via your migration tooling or manual execution in RQLite.
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
-- DeBros Gateway - Core schema (Phase 2)
|
||||
-- Orama Gateway - Core schema (Phase 2)
|
||||
-- Adds apps, nonces, subscriptions, refresh_tokens, audit_events, namespace_ownership
|
||||
-- SQLite/RQLite dialect
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
-- DeBros Gateway - Wallet to API Key linkage (Phase 3)
|
||||
-- Orama Gateway - Wallet to API Key linkage (Phase 3)
|
||||
-- Ensures one API key per (namespace, wallet) and enables lookup
|
||||
|
||||
BEGIN;
|
||||
|
||||
@ -169,10 +169,10 @@ func (creds *Credentials) UpdateLastUsed() {
|
||||
// GetDefaultGatewayURL returns the default gateway URL from environment config, env vars, or fallback
|
||||
func GetDefaultGatewayURL() string {
|
||||
// Check environment variables first (for backwards compatibility)
|
||||
if envURL := os.Getenv("DEBROS_GATEWAY_URL"); envURL != "" {
|
||||
if envURL := os.Getenv("ORAMA_GATEWAY_URL"); envURL != "" {
|
||||
return envURL
|
||||
}
|
||||
if envURL := os.Getenv("DEBROS_GATEWAY"); envURL != "" {
|
||||
if envURL := os.Getenv("ORAMA_GATEWAY"); envURL != "" {
|
||||
return envURL
|
||||
}
|
||||
|
||||
|
||||
@ -225,7 +225,7 @@ func (as *AuthServer) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "ok",
|
||||
"server": "debros-auth-callback",
|
||||
"server": "orama-auth-callback",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -115,8 +115,8 @@ func (cm *CertificateManager) generateCACertificate() ([]byte, []byte, error) {
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: "DeBros Network Root CA",
|
||||
Organization: []string{"DeBros"},
|
||||
CommonName: "Orama Network Root CA",
|
||||
Organization: []string{"Orama"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0), // 10 year validity
|
||||
|
||||
@ -66,7 +66,7 @@ func showAuthHelp() {
|
||||
fmt.Printf(" orama auth whoami # Check who you're logged in as\n")
|
||||
fmt.Printf(" orama auth logout # Clear all stored credentials\n\n")
|
||||
fmt.Printf("Environment Variables:\n")
|
||||
fmt.Printf(" DEBROS_GATEWAY_URL - Gateway URL (overrides environment config)\n\n")
|
||||
fmt.Printf(" ORAMA_GATEWAY_URL - Gateway URL (overrides environment config)\n\n")
|
||||
fmt.Printf("Authentication Flow (RootWallet):\n")
|
||||
fmt.Printf(" 1. Run 'orama auth login'\n")
|
||||
fmt.Printf(" 2. Your wallet address is read from RootWallet automatically\n")
|
||||
@ -295,7 +295,7 @@ func handleAuthStatus() {
|
||||
// Uses the active environment or allows entering a custom domain
|
||||
func promptForGatewayURL() string {
|
||||
// Check environment variable first (allows override without prompting)
|
||||
if url := os.Getenv("DEBROS_GATEWAY_URL"); url != "" {
|
||||
if url := os.Getenv("ORAMA_GATEWAY_URL"); url != "" {
|
||||
return url
|
||||
}
|
||||
|
||||
@ -346,7 +346,7 @@ func promptForGatewayURL() string {
|
||||
// Used by other commands that don't need interactive node selection
|
||||
func getGatewayURL() string {
|
||||
// Check environment variable first (for backwards compatibility)
|
||||
if url := os.Getenv("DEBROS_GATEWAY_URL"); url != "" {
|
||||
if url := os.Getenv("ORAMA_GATEWAY_URL"); url != "" {
|
||||
return url
|
||||
}
|
||||
|
||||
|
||||
80
pkg/cli/cluster/commands.go
Normal file
80
pkg/cli/cluster/commands.go
Normal file
@ -0,0 +1,80 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// HandleCommand handles cluster subcommands.
|
||||
func HandleCommand(args []string) {
|
||||
if len(args) == 0 {
|
||||
ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
subcommand := args[0]
|
||||
subargs := args[1:]
|
||||
|
||||
switch subcommand {
|
||||
case "status":
|
||||
HandleStatus(subargs)
|
||||
case "health":
|
||||
HandleHealth(subargs)
|
||||
case "rqlite":
|
||||
HandleRQLite(subargs)
|
||||
case "watch":
|
||||
HandleWatch(subargs)
|
||||
case "help":
|
||||
ShowHelp()
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown cluster subcommand: %s\n", subcommand)
|
||||
ShowHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// hasFlag checks if a flag is present in the args slice.
|
||||
func hasFlag(args []string, flag string) bool {
|
||||
for _, a := range args {
|
||||
if a == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getFlagValue returns the value of a flag from the args slice.
|
||||
// Returns empty string if the flag is not found or has no value.
|
||||
func getFlagValue(args []string, flag string) string {
|
||||
for i, a := range args {
|
||||
if a == flag && i+1 < len(args) {
|
||||
return args[i+1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ShowHelp displays help information for cluster commands.
|
||||
func ShowHelp() {
|
||||
fmt.Printf("Cluster Management Commands\n\n")
|
||||
fmt.Printf("Usage: orama cluster <subcommand> [options]\n\n")
|
||||
fmt.Printf("Subcommands:\n")
|
||||
fmt.Printf(" status - Show cluster node status (RQLite + Olric)\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --all - SSH into all nodes from remote-nodes.conf (TODO)\n")
|
||||
fmt.Printf(" health - Run cluster health checks\n")
|
||||
fmt.Printf(" rqlite <subcommand> - RQLite-specific commands\n")
|
||||
fmt.Printf(" status - Show detailed Raft state for local node\n")
|
||||
fmt.Printf(" voters - Show current voter list\n")
|
||||
fmt.Printf(" backup [--output FILE] - Trigger manual backup\n")
|
||||
fmt.Printf(" watch - Live cluster status monitor\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --interval SECONDS - Refresh interval (default: 10)\n\n")
|
||||
fmt.Printf("Examples:\n")
|
||||
fmt.Printf(" orama cluster status\n")
|
||||
fmt.Printf(" orama cluster health\n")
|
||||
fmt.Printf(" orama cluster rqlite status\n")
|
||||
fmt.Printf(" orama cluster rqlite voters\n")
|
||||
fmt.Printf(" orama cluster rqlite backup --output /tmp/backup.db\n")
|
||||
fmt.Printf(" orama cluster watch --interval 5\n")
|
||||
}
|
||||
244
pkg/cli/cluster/health.go
Normal file
244
pkg/cli/cluster/health.go
Normal file
@ -0,0 +1,244 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// checkResult represents the outcome of a single health check.
|
||||
type checkResult struct {
|
||||
Name string
|
||||
Status string // "PASS", "FAIL", "WARN"
|
||||
Detail string
|
||||
}
|
||||
|
||||
// HandleHealth handles the "orama cluster health" command.
|
||||
func HandleHealth(args []string) {
|
||||
fmt.Printf("Cluster Health Check\n")
|
||||
fmt.Printf("====================\n\n")
|
||||
|
||||
var results []checkResult
|
||||
|
||||
// Check 1: RQLite reachable
|
||||
status, err := queryRQLiteStatus()
|
||||
if err != nil {
|
||||
results = append(results, checkResult{
|
||||
Name: "RQLite reachable",
|
||||
Status: "FAIL",
|
||||
Detail: fmt.Sprintf("Cannot connect to RQLite: %v", err),
|
||||
})
|
||||
printHealthResults(results)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
results = append(results, checkResult{
|
||||
Name: "RQLite reachable",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("HTTP API responding on %s", status.HTTP.Address),
|
||||
})
|
||||
|
||||
// Check 2: Raft state is leader or follower (not candidate or shutdown)
|
||||
raftState := status.Store.Raft.State
|
||||
switch raftState {
|
||||
case "Leader", "Follower":
|
||||
results = append(results, checkResult{
|
||||
Name: "Raft state healthy",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("Node is %s", raftState),
|
||||
})
|
||||
case "Candidate":
|
||||
results = append(results, checkResult{
|
||||
Name: "Raft state healthy",
|
||||
Status: "WARN",
|
||||
Detail: "Node is Candidate (election in progress)",
|
||||
})
|
||||
default:
|
||||
results = append(results, checkResult{
|
||||
Name: "Raft state healthy",
|
||||
Status: "FAIL",
|
||||
Detail: fmt.Sprintf("Node is in unexpected state: %s", raftState),
|
||||
})
|
||||
}
|
||||
|
||||
// Check 3: Leader exists
|
||||
if status.Store.Raft.Leader != "" {
|
||||
results = append(results, checkResult{
|
||||
Name: "Leader exists",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("Leader: %s", status.Store.Raft.Leader),
|
||||
})
|
||||
} else {
|
||||
results = append(results, checkResult{
|
||||
Name: "Leader exists",
|
||||
Status: "FAIL",
|
||||
Detail: "No leader detected in Raft cluster",
|
||||
})
|
||||
}
|
||||
|
||||
// Check 4: Applied index is advancing (commit == applied means caught up)
|
||||
if status.Store.Raft.AppliedIndex >= status.Store.Raft.CommitIndex {
|
||||
results = append(results, checkResult{
|
||||
Name: "Log replication",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("Applied index (%d) >= commit index (%d)",
|
||||
status.Store.Raft.AppliedIndex, status.Store.Raft.CommitIndex),
|
||||
})
|
||||
} else {
|
||||
lag := status.Store.Raft.CommitIndex - status.Store.Raft.AppliedIndex
|
||||
severity := "WARN"
|
||||
if lag > 1000 {
|
||||
severity = "FAIL"
|
||||
}
|
||||
results = append(results, checkResult{
|
||||
Name: "Log replication",
|
||||
Status: severity,
|
||||
Detail: fmt.Sprintf("Applied index (%d) behind commit index (%d) by %d entries",
|
||||
status.Store.Raft.AppliedIndex, status.Store.Raft.CommitIndex, lag),
|
||||
})
|
||||
}
|
||||
|
||||
// Check 5: Query nodes to validate cluster membership
|
||||
nodes, err := queryRQLiteNodes(true)
|
||||
if err != nil {
|
||||
results = append(results, checkResult{
|
||||
Name: "Cluster nodes reachable",
|
||||
Status: "FAIL",
|
||||
Detail: fmt.Sprintf("Cannot query /nodes: %v", err),
|
||||
})
|
||||
} else {
|
||||
totalNodes := len(nodes)
|
||||
voters := 0
|
||||
nonVoters := 0
|
||||
reachable := 0
|
||||
leaders := 0
|
||||
|
||||
for _, node := range nodes {
|
||||
if node.Voter {
|
||||
voters++
|
||||
} else {
|
||||
nonVoters++
|
||||
}
|
||||
if node.Reachable {
|
||||
reachable++
|
||||
}
|
||||
if node.Leader {
|
||||
leaders++
|
||||
}
|
||||
}
|
||||
|
||||
// Check 5a: Node count
|
||||
results = append(results, checkResult{
|
||||
Name: "Cluster membership",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("%d nodes (%d voters, %d non-voters)", totalNodes, voters, nonVoters),
|
||||
})
|
||||
|
||||
// Check 5b: All nodes reachable
|
||||
if reachable == totalNodes {
|
||||
results = append(results, checkResult{
|
||||
Name: "All nodes reachable",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("%d/%d nodes reachable", reachable, totalNodes),
|
||||
})
|
||||
} else {
|
||||
unreachable := totalNodes - reachable
|
||||
results = append(results, checkResult{
|
||||
Name: "All nodes reachable",
|
||||
Status: "WARN",
|
||||
Detail: fmt.Sprintf("%d/%d nodes reachable (%d unreachable)", reachable, totalNodes, unreachable),
|
||||
})
|
||||
}
|
||||
|
||||
// Check 5c: Exactly one leader
|
||||
if leaders == 1 {
|
||||
results = append(results, checkResult{
|
||||
Name: "Single leader",
|
||||
Status: "PASS",
|
||||
Detail: "Exactly 1 leader in cluster",
|
||||
})
|
||||
} else if leaders == 0 {
|
||||
results = append(results, checkResult{
|
||||
Name: "Single leader",
|
||||
Status: "FAIL",
|
||||
Detail: "No leader found among nodes",
|
||||
})
|
||||
} else {
|
||||
results = append(results, checkResult{
|
||||
Name: "Single leader",
|
||||
Status: "FAIL",
|
||||
Detail: fmt.Sprintf("Multiple leaders detected: %d (split-brain?)", leaders),
|
||||
})
|
||||
}
|
||||
|
||||
// Check 5d: Quorum check (majority of voters must be reachable)
|
||||
quorum := (voters / 2) + 1
|
||||
reachableVoters := 0
|
||||
for _, node := range nodes {
|
||||
if node.Voter && node.Reachable {
|
||||
reachableVoters++
|
||||
}
|
||||
}
|
||||
if reachableVoters >= quorum {
|
||||
results = append(results, checkResult{
|
||||
Name: "Quorum healthy",
|
||||
Status: "PASS",
|
||||
Detail: fmt.Sprintf("%d/%d voters reachable (quorum requires %d)", reachableVoters, voters, quorum),
|
||||
})
|
||||
} else {
|
||||
results = append(results, checkResult{
|
||||
Name: "Quorum healthy",
|
||||
Status: "FAIL",
|
||||
Detail: fmt.Sprintf("%d/%d voters reachable (quorum requires %d)", reachableVoters, voters, quorum),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
printHealthResults(results)
|
||||
|
||||
// Exit with non-zero if any failures
|
||||
for _, r := range results {
|
||||
if r.Status == "FAIL" {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printHealthResults prints the health check results in a formatted table.
|
||||
func printHealthResults(results []checkResult) {
|
||||
// Find the longest check name for alignment
|
||||
maxName := 0
|
||||
for _, r := range results {
|
||||
if len(r.Name) > maxName {
|
||||
maxName = len(r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
indicator := " "
|
||||
switch r.Status {
|
||||
case "PASS":
|
||||
indicator = "PASS"
|
||||
case "FAIL":
|
||||
indicator = "FAIL"
|
||||
case "WARN":
|
||||
indicator = "WARN"
|
||||
}
|
||||
|
||||
fmt.Printf(" [%s] %-*s %s\n", indicator, maxName, r.Name, r.Detail)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Summary
|
||||
pass, fail, warn := 0, 0, 0
|
||||
for _, r := range results {
|
||||
switch r.Status {
|
||||
case "PASS":
|
||||
pass++
|
||||
case "FAIL":
|
||||
fail++
|
||||
case "WARN":
|
||||
warn++
|
||||
}
|
||||
}
|
||||
fmt.Printf("Summary: %d passed, %d failed, %d warnings\n", pass, fail, warn)
|
||||
}
|
||||
187
pkg/cli/cluster/rqlite.go
Normal file
187
pkg/cli/cluster/rqlite.go
Normal file
@ -0,0 +1,187 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandleRQLite handles the "orama cluster rqlite" subcommand group.
|
||||
func HandleRQLite(args []string) {
|
||||
if len(args) == 0 {
|
||||
showRQLiteHelp()
|
||||
return
|
||||
}
|
||||
|
||||
subcommand := args[0]
|
||||
subargs := args[1:]
|
||||
|
||||
switch subcommand {
|
||||
case "status":
|
||||
handleRQLiteStatus()
|
||||
case "voters":
|
||||
handleRQLiteVoters()
|
||||
case "backup":
|
||||
handleRQLiteBackup(subargs)
|
||||
case "help":
|
||||
showRQLiteHelp()
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown rqlite subcommand: %s\n", subcommand)
|
||||
showRQLiteHelp()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// handleRQLiteStatus shows detailed Raft state for the local node.
|
||||
func handleRQLiteStatus() {
|
||||
fmt.Printf("RQLite Raft Status\n")
|
||||
fmt.Printf("==================\n\n")
|
||||
|
||||
status, err := queryRQLiteStatus()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Node Configuration\n")
|
||||
fmt.Printf(" Node ID: %s\n", status.Store.NodeID)
|
||||
fmt.Printf(" Raft Address: %s\n", status.Store.Address)
|
||||
fmt.Printf(" HTTP Address: %s\n", status.HTTP.Address)
|
||||
fmt.Printf(" Data Directory: %s\n", status.Store.Dir)
|
||||
fmt.Println()
|
||||
|
||||
fmt.Printf("Raft State\n")
|
||||
fmt.Printf(" State: %s\n", strings.ToUpper(status.Store.Raft.State))
|
||||
fmt.Printf(" Current Term: %d\n", status.Store.Raft.Term)
|
||||
fmt.Printf(" Applied Index: %d\n", status.Store.Raft.AppliedIndex)
|
||||
fmt.Printf(" Commit Index: %d\n", status.Store.Raft.CommitIndex)
|
||||
fmt.Printf(" Leader: %s\n", status.Store.Raft.Leader)
|
||||
|
||||
if status.Store.Raft.AppliedIndex < status.Store.Raft.CommitIndex {
|
||||
lag := status.Store.Raft.CommitIndex - status.Store.Raft.AppliedIndex
|
||||
fmt.Printf(" Replication Lag: %d entries behind\n", lag)
|
||||
} else {
|
||||
fmt.Printf(" Replication Lag: none (fully caught up)\n")
|
||||
}
|
||||
|
||||
if status.Node.Uptime != "" {
|
||||
fmt.Printf(" Uptime: %s\n", status.Node.Uptime)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// handleRQLiteVoters shows the current voter list from /nodes.
|
||||
func handleRQLiteVoters() {
|
||||
fmt.Printf("RQLite Cluster Voters\n")
|
||||
fmt.Printf("=====================\n\n")
|
||||
|
||||
nodes, err := queryRQLiteNodes(true)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
voters := 0
|
||||
nonVoters := 0
|
||||
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s\n",
|
||||
"NODE ID", "ADDRESS", "ROLE", "LEADER", "REACHABLE")
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s\n",
|
||||
strings.Repeat("-", 20),
|
||||
strings.Repeat("-", 30),
|
||||
strings.Repeat("-", 8),
|
||||
strings.Repeat("-", 10),
|
||||
strings.Repeat("-", 10))
|
||||
|
||||
for id, node := range nodes {
|
||||
nodeID := id
|
||||
if len(nodeID) > 20 {
|
||||
nodeID = nodeID[:17] + "..."
|
||||
}
|
||||
|
||||
role := "non-voter"
|
||||
if node.Voter {
|
||||
role = "voter"
|
||||
voters++
|
||||
} else {
|
||||
nonVoters++
|
||||
}
|
||||
|
||||
leader := "no"
|
||||
if node.Leader {
|
||||
leader = "yes"
|
||||
}
|
||||
|
||||
reachable := "no"
|
||||
if node.Reachable {
|
||||
reachable = "yes"
|
||||
}
|
||||
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s\n",
|
||||
nodeID, node.Address, role, leader, reachable)
|
||||
}
|
||||
|
||||
fmt.Printf("\nTotal: %d voters, %d non-voters\n", voters, nonVoters)
|
||||
quorum := (voters / 2) + 1
|
||||
fmt.Printf("Quorum requirement: %d/%d voters\n", quorum, voters)
|
||||
}
|
||||
|
||||
// handleRQLiteBackup triggers a manual backup via the RQLite backup endpoint.
|
||||
func handleRQLiteBackup(args []string) {
|
||||
outputFile := getFlagValue(args, "--output")
|
||||
if outputFile == "" {
|
||||
outputFile = fmt.Sprintf("rqlite-backup-%s.db", time.Now().Format("20060102-150405"))
|
||||
}
|
||||
|
||||
fmt.Printf("RQLite Backup\n")
|
||||
fmt.Printf("=============\n\n")
|
||||
fmt.Printf("Requesting backup from %s/db/backup ...\n", rqliteBaseURL)
|
||||
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
resp, err := client.Get(rqliteBaseURL + "/db/backup")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: cannot connect to RQLite: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Fprintf(os.Stderr, "Error: backup request returned HTTP %d: %s\n", resp.StatusCode, string(body))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
outFile, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: cannot create output file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
written, err := io.Copy(outFile, resp.Body)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to write backup: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Backup saved to: %s (%d bytes)\n", outputFile, written)
|
||||
}
|
||||
|
||||
// showRQLiteHelp displays help for rqlite subcommands.
|
||||
func showRQLiteHelp() {
|
||||
fmt.Printf("RQLite Commands\n\n")
|
||||
fmt.Printf("Usage: orama cluster rqlite <subcommand> [options]\n\n")
|
||||
fmt.Printf("Subcommands:\n")
|
||||
fmt.Printf(" status - Show detailed Raft state for local node\n")
|
||||
fmt.Printf(" voters - Show current voter list from cluster\n")
|
||||
fmt.Printf(" backup - Trigger manual database backup\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --output FILE - Output file path (default: rqlite-backup-<timestamp>.db)\n\n")
|
||||
fmt.Printf("Examples:\n")
|
||||
fmt.Printf(" orama cluster rqlite status\n")
|
||||
fmt.Printf(" orama cluster rqlite voters\n")
|
||||
fmt.Printf(" orama cluster rqlite backup --output /tmp/backup.db\n")
|
||||
}
|
||||
248
pkg/cli/cluster/status.go
Normal file
248
pkg/cli/cluster/status.go
Normal file
@ -0,0 +1,248 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rqliteBaseURL = "http://localhost:5001"
|
||||
httpTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// rqliteStatus represents the relevant fields from the RQLite /status endpoint.
|
||||
type rqliteStatus struct {
|
||||
Store struct {
|
||||
Raft struct {
|
||||
State string `json:"state"`
|
||||
AppliedIndex uint64 `json:"applied_index"`
|
||||
CommitIndex uint64 `json:"commit_index"`
|
||||
Term uint64 `json:"current_term"`
|
||||
Leader string `json:"leader"`
|
||||
} `json:"raft"`
|
||||
Dir string `json:"dir"`
|
||||
NodeID string `json:"node_id"`
|
||||
Address string `json:"addr"`
|
||||
} `json:"store"`
|
||||
HTTP struct {
|
||||
Address string `json:"addr"`
|
||||
} `json:"http"`
|
||||
Node struct {
|
||||
Uptime string `json:"uptime"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
// rqliteNode represents a node from the /nodes endpoint.
|
||||
type rqliteNode struct {
|
||||
ID string `json:"id"`
|
||||
Address string `json:"addr"`
|
||||
Leader bool `json:"leader"`
|
||||
Voter bool `json:"voter"`
|
||||
Reachable bool `json:"reachable"`
|
||||
Time float64 `json:"time"`
|
||||
TimeS string `json:"time_s"`
|
||||
}
|
||||
|
||||
// HandleStatus handles the "orama cluster status" command.
|
||||
func HandleStatus(args []string) {
|
||||
if hasFlag(args, "--all") {
|
||||
fmt.Printf("Remote node aggregation via SSH is not yet implemented.\n")
|
||||
fmt.Printf("Currently showing local node status only.\n\n")
|
||||
}
|
||||
|
||||
fmt.Printf("Cluster Status\n")
|
||||
fmt.Printf("==============\n\n")
|
||||
|
||||
// Query RQLite status
|
||||
status, err := queryRQLiteStatus()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error querying RQLite status: %v\n", err)
|
||||
fmt.Printf("RQLite may not be running on this node.\n\n")
|
||||
} else {
|
||||
printLocalStatus(status)
|
||||
}
|
||||
|
||||
// Query RQLite nodes
|
||||
nodes, err := queryRQLiteNodes(true)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error querying RQLite nodes: %v\n", err)
|
||||
} else {
|
||||
printNodesTable(nodes)
|
||||
}
|
||||
|
||||
// Query Olric status (best-effort)
|
||||
printOlricStatus()
|
||||
}
|
||||
|
||||
// queryRQLiteStatus queries the local RQLite /status endpoint.
|
||||
func queryRQLiteStatus() (*rqliteStatus, error) {
|
||||
client := &http.Client{Timeout: httpTimeout}
|
||||
resp, err := client.Get(rqliteBaseURL + "/status")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to RQLite: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
|
||||
var status rqliteStatus
|
||||
if err := json.Unmarshal(body, &status); err != nil {
|
||||
return nil, fmt.Errorf("parse response: %w", err)
|
||||
}
|
||||
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// queryRQLiteNodes queries the local RQLite /nodes endpoint.
|
||||
// If includeNonVoters is true, appends ?nonvoters to the query.
|
||||
func queryRQLiteNodes(includeNonVoters bool) (map[string]*rqliteNode, error) {
|
||||
client := &http.Client{Timeout: httpTimeout}
|
||||
|
||||
url := rqliteBaseURL + "/nodes"
|
||||
if includeNonVoters {
|
||||
url += "?nonvoters"
|
||||
}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect to RQLite: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
|
||||
var nodes map[string]*rqliteNode
|
||||
if err := json.Unmarshal(body, &nodes); err != nil {
|
||||
return nil, fmt.Errorf("parse response: %w", err)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// printLocalStatus prints the local node's RQLite status.
|
||||
func printLocalStatus(s *rqliteStatus) {
|
||||
fmt.Printf("Local Node\n")
|
||||
fmt.Printf(" Node ID: %s\n", s.Store.NodeID)
|
||||
fmt.Printf(" Raft Address: %s\n", s.Store.Address)
|
||||
fmt.Printf(" HTTP Address: %s\n", s.HTTP.Address)
|
||||
fmt.Printf(" Raft State: %s\n", strings.ToUpper(s.Store.Raft.State))
|
||||
fmt.Printf(" Raft Term: %d\n", s.Store.Raft.Term)
|
||||
fmt.Printf(" Applied Index: %d\n", s.Store.Raft.AppliedIndex)
|
||||
fmt.Printf(" Commit Index: %d\n", s.Store.Raft.CommitIndex)
|
||||
fmt.Printf(" Leader: %s\n", s.Store.Raft.Leader)
|
||||
if s.Node.Uptime != "" {
|
||||
fmt.Printf(" Uptime: %s\n", s.Node.Uptime)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// printNodesTable prints a formatted table of all cluster nodes.
|
||||
func printNodesTable(nodes map[string]*rqliteNode) {
|
||||
if len(nodes) == 0 {
|
||||
fmt.Printf("No nodes found in cluster.\n\n")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Cluster Nodes (%d total)\n", len(nodes))
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s %-12s\n",
|
||||
"NODE ID", "ADDRESS", "VOTER", "LEADER", "REACHABLE", "LATENCY")
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s %-12s\n",
|
||||
strings.Repeat("-", 20),
|
||||
strings.Repeat("-", 30),
|
||||
strings.Repeat("-", 8),
|
||||
strings.Repeat("-", 10),
|
||||
strings.Repeat("-", 10),
|
||||
strings.Repeat("-", 12))
|
||||
|
||||
for id, node := range nodes {
|
||||
nodeID := id
|
||||
if len(nodeID) > 20 {
|
||||
nodeID = nodeID[:17] + "..."
|
||||
}
|
||||
|
||||
voter := "no"
|
||||
if node.Voter {
|
||||
voter = "yes"
|
||||
}
|
||||
|
||||
leader := "no"
|
||||
if node.Leader {
|
||||
leader = "yes"
|
||||
}
|
||||
|
||||
reachable := "no"
|
||||
if node.Reachable {
|
||||
reachable = "yes"
|
||||
}
|
||||
|
||||
latency := "-"
|
||||
if node.TimeS != "" {
|
||||
latency = node.TimeS
|
||||
} else if node.Time > 0 {
|
||||
latency = fmt.Sprintf("%.3fs", node.Time)
|
||||
}
|
||||
|
||||
fmt.Printf("%-20s %-30s %-8s %-10s %-10s %-12s\n",
|
||||
nodeID, node.Address, voter, leader, reachable, latency)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// printOlricStatus attempts to query the local Olric status endpoint.
|
||||
func printOlricStatus() {
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := client.Get("http://localhost:3320/")
|
||||
if err != nil {
|
||||
fmt.Printf("Olric: not reachable on localhost:3320 (%v)\n\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("Olric: reachable but could not read response\n\n")
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
fmt.Printf("Olric: reachable (HTTP %d)\n", resp.StatusCode)
|
||||
// Try to parse as JSON for a nicer display
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(body, &data); err == nil {
|
||||
for key, val := range data {
|
||||
fmt.Printf(" %s: %v\n", key, val)
|
||||
}
|
||||
} else {
|
||||
// Not JSON, print raw (truncated)
|
||||
raw := strings.TrimSpace(string(body))
|
||||
if len(raw) > 200 {
|
||||
raw = raw[:200] + "..."
|
||||
}
|
||||
if raw != "" {
|
||||
fmt.Printf(" Response: %s\n", raw)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Olric: reachable but returned HTTP %d\n", resp.StatusCode)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
136
pkg/cli/cluster/watch.go
Normal file
136
pkg/cli/cluster/watch.go
Normal file
@ -0,0 +1,136 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandleWatch handles the "orama cluster watch" command.
|
||||
// It polls RQLite status and nodes at a configurable interval and reprints a summary.
|
||||
func HandleWatch(args []string) {
|
||||
interval := 10 * time.Second
|
||||
|
||||
// Parse --interval flag
|
||||
intervalStr := getFlagValue(args, "--interval")
|
||||
if intervalStr != "" {
|
||||
secs, err := strconv.Atoi(intervalStr)
|
||||
if err != nil || secs < 1 {
|
||||
fmt.Fprintf(os.Stderr, "Error: --interval must be a positive integer (seconds)\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
interval = time.Duration(secs) * time.Second
|
||||
}
|
||||
|
||||
// Set up signal handling for clean exit
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
fmt.Printf("Watching cluster status (interval: %s, Ctrl+C to exit)\n\n", interval)
|
||||
|
||||
// Initial render
|
||||
renderWatchScreen()
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
renderWatchScreen()
|
||||
case <-sigCh:
|
||||
fmt.Printf("\nWatch stopped.\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// renderWatchScreen clears the terminal and prints a summary of cluster state.
|
||||
func renderWatchScreen() {
|
||||
// Clear screen using ANSI escape codes
|
||||
fmt.Print("\033[2J\033[H")
|
||||
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
fmt.Printf("Cluster Watch [%s]\n", now)
|
||||
fmt.Printf("=======================================\n\n")
|
||||
|
||||
// Query RQLite status
|
||||
status, err := queryRQLiteStatus()
|
||||
if err != nil {
|
||||
fmt.Printf("RQLite: UNREACHABLE (%v)\n\n", err)
|
||||
} else {
|
||||
fmt.Printf("Local Node: %s\n", status.Store.NodeID)
|
||||
fmt.Printf(" State: %-10s Term: %-6d Applied: %-8d Commit: %-8d\n",
|
||||
strings.ToUpper(status.Store.Raft.State),
|
||||
status.Store.Raft.Term,
|
||||
status.Store.Raft.AppliedIndex,
|
||||
status.Store.Raft.CommitIndex)
|
||||
fmt.Printf(" Leader: %s\n", status.Store.Raft.Leader)
|
||||
if status.Node.Uptime != "" {
|
||||
fmt.Printf(" Uptime: %s\n", status.Node.Uptime)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Query nodes
|
||||
nodes, err := queryRQLiteNodes(true)
|
||||
if err != nil {
|
||||
fmt.Printf("Nodes: UNAVAILABLE (%v)\n\n", err)
|
||||
} else {
|
||||
total := len(nodes)
|
||||
voters := 0
|
||||
reachable := 0
|
||||
for _, n := range nodes {
|
||||
if n.Voter {
|
||||
voters++
|
||||
}
|
||||
if n.Reachable {
|
||||
reachable++
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Cluster: %d nodes (%d voters), %d/%d reachable\n\n",
|
||||
total, voters, reachable, total)
|
||||
|
||||
// Compact table
|
||||
fmt.Printf("%-18s %-28s %-7s %-7s %-7s\n",
|
||||
"ID", "ADDRESS", "VOTER", "LEADER", "UP")
|
||||
fmt.Printf("%-18s %-28s %-7s %-7s %-7s\n",
|
||||
strings.Repeat("-", 18),
|
||||
strings.Repeat("-", 28),
|
||||
strings.Repeat("-", 7),
|
||||
strings.Repeat("-", 7),
|
||||
strings.Repeat("-", 7))
|
||||
|
||||
for id, node := range nodes {
|
||||
nodeID := id
|
||||
if len(nodeID) > 18 {
|
||||
nodeID = nodeID[:15] + "..."
|
||||
}
|
||||
|
||||
voter := " "
|
||||
if node.Voter {
|
||||
voter = "yes"
|
||||
}
|
||||
|
||||
leader := " "
|
||||
if node.Leader {
|
||||
leader = "yes"
|
||||
}
|
||||
|
||||
up := "no"
|
||||
if node.Reachable {
|
||||
up = "yes"
|
||||
}
|
||||
|
||||
fmt.Printf("%-18s %-28s %-7s %-7s %-7s\n",
|
||||
nodeID, node.Address, voter, leader, up)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\nPress Ctrl+C to exit\n")
|
||||
}
|
||||
10
pkg/cli/cluster_commands.go
Normal file
10
pkg/cli/cluster_commands.go
Normal file
@ -0,0 +1,10 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/DeBrosOfficial/network/pkg/cli/cluster"
|
||||
)
|
||||
|
||||
// HandleClusterCommand handles cluster management commands.
|
||||
func HandleClusterCommand(args []string) {
|
||||
cluster.HandleCommand(args)
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"github.com/DeBrosOfficial/network/pkg/config"
|
||||
)
|
||||
|
||||
// Environment represents a DeBros network environment
|
||||
// Environment represents a Orama network environment
|
||||
type Environment struct {
|
||||
Name string `json:"name"`
|
||||
GatewayURL string `json:"gateway_url"`
|
||||
|
||||
@ -89,15 +89,11 @@ func ShowHelp() {
|
||||
fmt.Printf(" --ipfs-addrs ADDRS - IPFS swarm addresses (auto-discovered)\n")
|
||||
fmt.Printf(" --ipfs-cluster-peer ID - IPFS Cluster peer ID (auto-discovered)\n")
|
||||
fmt.Printf(" --ipfs-cluster-addrs ADDRS - IPFS Cluster addresses (auto-discovered)\n")
|
||||
fmt.Printf(" --branch BRANCH - Git branch to use (main or nightly, default: main)\n")
|
||||
fmt.Printf(" --no-pull - Skip git clone/pull, use existing /home/debros/src\n")
|
||||
fmt.Printf(" --ignore-resource-checks - Skip disk/RAM/CPU prerequisite validation\n")
|
||||
fmt.Printf(" --dry-run - Show what would be done without making changes\n")
|
||||
fmt.Printf(" upgrade - Upgrade existing installation (requires root/sudo)\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --restart - Automatically restart services after upgrade\n")
|
||||
fmt.Printf(" --branch BRANCH - Git branch to use (main or nightly)\n")
|
||||
fmt.Printf(" --no-pull - Skip git clone/pull, use existing source\n")
|
||||
fmt.Printf(" migrate - Migrate from old unified setup (requires root/sudo)\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --dry-run - Show what would be migrated without making changes\n")
|
||||
|
||||
@ -30,7 +30,7 @@ type Orchestrator struct {
|
||||
|
||||
// NewOrchestrator creates a new install orchestrator
|
||||
func NewOrchestrator(flags *Flags) (*Orchestrator, error) {
|
||||
oramaHome := "/home/debros"
|
||||
oramaHome := "/home/orama"
|
||||
oramaDir := oramaHome + "/.orama"
|
||||
|
||||
// Normalize peers
|
||||
@ -547,9 +547,9 @@ func (o *Orchestrator) installNamespaceTemplates() error {
|
||||
systemdDir := "/etc/systemd/system"
|
||||
|
||||
templates := []string{
|
||||
"debros-namespace-rqlite@.service",
|
||||
"debros-namespace-olric@.service",
|
||||
"debros-namespace-gateway@.service",
|
||||
"orama-namespace-rqlite@.service",
|
||||
"orama-namespace-olric@.service",
|
||||
"orama-namespace-gateway@.service",
|
||||
}
|
||||
|
||||
installedCount := 0
|
||||
|
||||
215
pkg/cli/production/install/remote.go
Normal file
215
pkg/cli/production/install/remote.go
Normal file
@ -0,0 +1,215 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DeBrosOfficial/network/pkg/inspector"
|
||||
)
|
||||
|
||||
// RemoteOrchestrator orchestrates a remote install via SSH.
|
||||
// It uploads the source archive, extracts it on the VPS, and runs
|
||||
// the actual install command remotely.
|
||||
type RemoteOrchestrator struct {
|
||||
flags *Flags
|
||||
node inspector.Node
|
||||
archive string
|
||||
}
|
||||
|
||||
// NewRemoteOrchestrator creates a new remote orchestrator.
|
||||
// It resolves SSH credentials and checks prerequisites.
|
||||
func NewRemoteOrchestrator(flags *Flags) (*RemoteOrchestrator, error) {
|
||||
if flags.VpsIP == "" {
|
||||
return nil, fmt.Errorf("--vps-ip is required\nExample: orama install --vps-ip 1.2.3.4 --nameserver --domain orama-testnet.network")
|
||||
}
|
||||
|
||||
// Check source archive exists
|
||||
if _, err := os.Stat(sourceArchivePath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("source archive not found at %s\nRun: make build-linux && ./scripts/generate-source-archive.sh", sourceArchivePath)
|
||||
}
|
||||
|
||||
// Resolve SSH credentials
|
||||
node, err := resolveSSHCredentials(flags.VpsIP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve SSH credentials: %w", err)
|
||||
}
|
||||
|
||||
return &RemoteOrchestrator{
|
||||
flags: flags,
|
||||
node: node,
|
||||
archive: sourceArchivePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Execute runs the full remote install process.
|
||||
func (r *RemoteOrchestrator) Execute() error {
|
||||
fmt.Printf("Installing on %s via SSH (%s@%s)...\n\n", r.flags.VpsIP, r.node.User, r.node.Host)
|
||||
|
||||
// Step 1: Upload archive
|
||||
fmt.Printf("Uploading source archive...\n")
|
||||
if err := r.uploadArchive(); err != nil {
|
||||
return fmt.Errorf("upload failed: %w", err)
|
||||
}
|
||||
fmt.Printf(" Done.\n\n")
|
||||
|
||||
// Step 2: Extract on VPS
|
||||
fmt.Printf("Extracting on VPS...\n")
|
||||
if err := r.extractOnVPS(); err != nil {
|
||||
return fmt.Errorf("extract failed: %w", err)
|
||||
}
|
||||
fmt.Printf(" Done.\n\n")
|
||||
|
||||
// Step 3: Run remote install
|
||||
fmt.Printf("Running install on VPS...\n\n")
|
||||
if err := r.runRemoteInstall(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// uploadArchive copies the source archive to the VPS.
|
||||
func (r *RemoteOrchestrator) uploadArchive() error {
|
||||
return uploadFile(r.node, r.archive, "/tmp/network-source.tar.gz")
|
||||
}
|
||||
|
||||
// extractOnVPS runs extract-deploy.sh on the VPS.
|
||||
func (r *RemoteOrchestrator) extractOnVPS() error {
|
||||
// Extract source archive and install only the CLI binary.
|
||||
// All other binaries are built from source on the VPS during install.
|
||||
extractCmd := r.sudoPrefix() + "bash -c '" +
|
||||
`ARCHIVE="/tmp/network-source.tar.gz" && ` +
|
||||
`SRC_DIR="/home/orama/src" && ` +
|
||||
`BIN_DIR="/home/orama/bin" && ` +
|
||||
`id -u orama &>/dev/null || useradd -m -s /bin/bash orama && ` +
|
||||
`rm -rf "$SRC_DIR" && mkdir -p "$SRC_DIR" "$BIN_DIR" && ` +
|
||||
`tar xzf "$ARCHIVE" -C "$SRC_DIR" && ` +
|
||||
`chown -R orama:orama "$SRC_DIR" && ` +
|
||||
// Install pre-built CLI binary (only binary cross-compiled locally)
|
||||
`if [ -f "$SRC_DIR/bin-linux/orama" ]; then ` +
|
||||
`cp "$SRC_DIR/bin-linux/orama" /usr/local/bin/orama && ` +
|
||||
`chmod +x /usr/local/bin/orama; fi && ` +
|
||||
`chown -R orama:orama "$BIN_DIR" && ` +
|
||||
`echo "Extract complete."` +
|
||||
"'"
|
||||
|
||||
return runSSHStreaming(r.node, extractCmd)
|
||||
}
|
||||
|
||||
// runRemoteInstall executes `orama install` on the VPS.
|
||||
func (r *RemoteOrchestrator) runRemoteInstall() error {
|
||||
cmd := r.buildRemoteCommand()
|
||||
return runSSHStreaming(r.node, cmd)
|
||||
}
|
||||
|
||||
// buildRemoteCommand constructs the `sudo orama install` command string
|
||||
// with all flags passed through.
|
||||
func (r *RemoteOrchestrator) buildRemoteCommand() string {
|
||||
var args []string
|
||||
if r.node.User != "root" {
|
||||
args = append(args, "sudo")
|
||||
}
|
||||
args = append(args, "orama", "install")
|
||||
|
||||
args = append(args, "--vps-ip", r.flags.VpsIP)
|
||||
|
||||
if r.flags.Domain != "" {
|
||||
args = append(args, "--domain", r.flags.Domain)
|
||||
}
|
||||
if r.flags.BaseDomain != "" {
|
||||
args = append(args, "--base-domain", r.flags.BaseDomain)
|
||||
}
|
||||
if r.flags.Nameserver {
|
||||
args = append(args, "--nameserver")
|
||||
}
|
||||
if r.flags.JoinAddress != "" {
|
||||
args = append(args, "--join", r.flags.JoinAddress)
|
||||
}
|
||||
if r.flags.Token != "" {
|
||||
args = append(args, "--token", r.flags.Token)
|
||||
}
|
||||
if r.flags.Force {
|
||||
args = append(args, "--force")
|
||||
}
|
||||
if r.flags.SkipChecks {
|
||||
args = append(args, "--skip-checks")
|
||||
}
|
||||
if r.flags.SkipFirewall {
|
||||
args = append(args, "--skip-firewall")
|
||||
}
|
||||
if r.flags.DryRun {
|
||||
args = append(args, "--dry-run")
|
||||
}
|
||||
|
||||
// Anyone relay flags
|
||||
if r.flags.AnyoneRelay {
|
||||
args = append(args, "--anyone-relay")
|
||||
}
|
||||
if r.flags.AnyoneClient {
|
||||
args = append(args, "--anyone-client")
|
||||
}
|
||||
if r.flags.AnyoneExit {
|
||||
args = append(args, "--anyone-exit")
|
||||
}
|
||||
if r.flags.AnyoneMigrate {
|
||||
args = append(args, "--anyone-migrate")
|
||||
}
|
||||
if r.flags.AnyoneNickname != "" {
|
||||
args = append(args, "--anyone-nickname", r.flags.AnyoneNickname)
|
||||
}
|
||||
if r.flags.AnyoneContact != "" {
|
||||
args = append(args, "--anyone-contact", r.flags.AnyoneContact)
|
||||
}
|
||||
if r.flags.AnyoneWallet != "" {
|
||||
args = append(args, "--anyone-wallet", r.flags.AnyoneWallet)
|
||||
}
|
||||
if r.flags.AnyoneORPort != 9001 {
|
||||
args = append(args, "--anyone-orport", strconv.Itoa(r.flags.AnyoneORPort))
|
||||
}
|
||||
if r.flags.AnyoneFamily != "" {
|
||||
args = append(args, "--anyone-family", r.flags.AnyoneFamily)
|
||||
}
|
||||
if r.flags.AnyoneBandwidth != 30 {
|
||||
args = append(args, "--anyone-bandwidth", strconv.Itoa(r.flags.AnyoneBandwidth))
|
||||
}
|
||||
if r.flags.AnyoneAccounting != 0 {
|
||||
args = append(args, "--anyone-accounting", strconv.Itoa(r.flags.AnyoneAccounting))
|
||||
}
|
||||
|
||||
return joinShellArgs(args)
|
||||
}
|
||||
|
||||
// sudoPrefix returns "sudo " for non-root SSH users, empty for root.
|
||||
func (r *RemoteOrchestrator) sudoPrefix() string {
|
||||
if r.node.User == "root" {
|
||||
return ""
|
||||
}
|
||||
return "sudo "
|
||||
}
|
||||
|
||||
// joinShellArgs joins arguments, quoting those with special characters.
|
||||
func joinShellArgs(args []string) string {
|
||||
var parts []string
|
||||
for _, a := range args {
|
||||
if needsQuoting(a) {
|
||||
parts = append(parts, "'"+a+"'")
|
||||
} else {
|
||||
parts = append(parts, a)
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// needsQuoting returns true if the string contains characters
|
||||
// that need shell quoting.
|
||||
func needsQuoting(s string) bool {
|
||||
for _, c := range s {
|
||||
switch c {
|
||||
case ' ', '$', '!', '&', '(', ')', '<', '>', '|', ';', '"', '`', '\\', '#', '^', '*', '?', '{', '}', '[', ']', '~':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
142
pkg/cli/production/install/ssh.go
Normal file
142
pkg/cli/production/install/ssh.go
Normal file
@ -0,0 +1,142 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/DeBrosOfficial/network/pkg/inspector"
|
||||
)
|
||||
|
||||
const sourceArchivePath = "/tmp/network-source.tar.gz"
|
||||
|
||||
// resolveSSHCredentials finds SSH credentials for the given VPS IP.
|
||||
// First checks remote-nodes.conf, then prompts interactively.
|
||||
func resolveSSHCredentials(vpsIP string) (inspector.Node, error) {
|
||||
confPath := findRemoteNodesConf()
|
||||
if confPath != "" {
|
||||
nodes, err := inspector.LoadNodes(confPath)
|
||||
if err == nil {
|
||||
for _, n := range nodes {
|
||||
if n.Host == vpsIP {
|
||||
// Expand ~ in SSH key path
|
||||
if n.SSHKey != "" && strings.HasPrefix(n.SSHKey, "~") {
|
||||
home, _ := os.UserHomeDir()
|
||||
n.SSHKey = filepath.Join(home, n.SSHKey[1:])
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found in config — prompt interactively
|
||||
return promptSSHCredentials(vpsIP), nil
|
||||
}
|
||||
|
||||
// findRemoteNodesConf searches for the remote-nodes.conf file.
|
||||
func findRemoteNodesConf() string {
|
||||
candidates := []string{
|
||||
"scripts/remote-nodes.conf",
|
||||
"../scripts/remote-nodes.conf",
|
||||
"network/scripts/remote-nodes.conf",
|
||||
}
|
||||
for _, c := range candidates {
|
||||
if _, err := os.Stat(c); err == nil {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// promptSSHCredentials asks the user for SSH credentials interactively.
|
||||
func promptSSHCredentials(vpsIP string) inspector.Node {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Printf("\nSSH credentials for %s\n", vpsIP)
|
||||
fmt.Print(" SSH user (default: ubuntu): ")
|
||||
user, _ := reader.ReadString('\n')
|
||||
user = strings.TrimSpace(user)
|
||||
if user == "" {
|
||||
user = "ubuntu"
|
||||
}
|
||||
|
||||
fmt.Print(" SSH password: ")
|
||||
password, _ := reader.ReadString('\n')
|
||||
password = strings.TrimSpace(password)
|
||||
|
||||
return inspector.Node{
|
||||
User: user,
|
||||
Host: vpsIP,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// uploadFile copies a local file to a remote host via SCP.
|
||||
func uploadFile(node inspector.Node, localPath, remotePath string) error {
|
||||
dest := fmt.Sprintf("%s@%s:%s", node.User, node.Host, remotePath)
|
||||
|
||||
var cmd *exec.Cmd
|
||||
if node.SSHKey != "" {
|
||||
cmd = exec.Command("scp",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "ConnectTimeout=10",
|
||||
"-i", node.SSHKey,
|
||||
localPath, dest,
|
||||
)
|
||||
} else {
|
||||
if _, err := exec.LookPath("sshpass"); err != nil {
|
||||
return fmt.Errorf("sshpass not found — install it: brew install hudochenkov/sshpass/sshpass")
|
||||
}
|
||||
cmd = exec.Command("sshpass", "-p", node.Password,
|
||||
"scp",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "ConnectTimeout=10",
|
||||
localPath, dest,
|
||||
)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("SCP failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// runSSHStreaming executes a command on a remote host via SSH,
|
||||
// streaming stdout/stderr to the local terminal in real-time.
|
||||
func runSSHStreaming(node inspector.Node, command string) error {
|
||||
var cmd *exec.Cmd
|
||||
if node.SSHKey != "" {
|
||||
cmd = exec.Command("ssh",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "ConnectTimeout=10",
|
||||
"-i", node.SSHKey,
|
||||
fmt.Sprintf("%s@%s", node.User, node.Host),
|
||||
command,
|
||||
)
|
||||
} else {
|
||||
cmd = exec.Command("sshpass", "-p", node.Password,
|
||||
"ssh",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "ConnectTimeout=10",
|
||||
fmt.Sprintf("%s@%s", node.User, node.Host),
|
||||
command,
|
||||
)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin // Allow password prompts from remote sudo
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("SSH command failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ func Handle(args []string) {
|
||||
|
||||
// readNodeDomain reads the domain from the node config file
|
||||
func readNodeDomain() (string, error) {
|
||||
configPath := "/home/debros/.orama/configs/node.yaml"
|
||||
configPath := "/home/orama/.orama/configs/node.yaml"
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read config: %w", err)
|
||||
|
||||
143
pkg/cli/production/lifecycle/post_upgrade.go
Normal file
143
pkg/cli/production/lifecycle/post_upgrade.go
Normal file
@ -0,0 +1,143 @@
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/DeBrosOfficial/network/pkg/cli/utils"
|
||||
)
|
||||
|
||||
// HandlePostUpgrade brings the node back online after an upgrade:
|
||||
// 1. Resets failed + unmasks + enables all services
|
||||
// 2. Starts services in dependency order
|
||||
// 3. Waits for global RQLite to be ready
|
||||
// 4. Waits for each namespace RQLite to be ready
|
||||
// 5. Removes maintenance flag
|
||||
func HandlePostUpgrade() {
|
||||
if os.Geteuid() != 0 {
|
||||
fmt.Fprintf(os.Stderr, "Error: post-upgrade must be run as root (use sudo)\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Post-upgrade: bringing node back online...\n")
|
||||
|
||||
// 1. Get all services
|
||||
services := utils.GetProductionServices()
|
||||
if len(services) == 0 {
|
||||
fmt.Printf(" Warning: no Orama services found\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Reset failed state
|
||||
resetArgs := []string{"reset-failed"}
|
||||
resetArgs = append(resetArgs, services...)
|
||||
exec.Command("systemctl", resetArgs...).Run()
|
||||
|
||||
// Unmask and enable all services
|
||||
for _, svc := range services {
|
||||
masked, err := utils.IsServiceMasked(svc)
|
||||
if err == nil && masked {
|
||||
exec.Command("systemctl", "unmask", svc).Run()
|
||||
}
|
||||
enabled, err := utils.IsServiceEnabled(svc)
|
||||
if err == nil && !enabled {
|
||||
exec.Command("systemctl", "enable", svc).Run()
|
||||
}
|
||||
}
|
||||
fmt.Printf(" Services reset and enabled\n")
|
||||
|
||||
// 2. Start services in dependency order
|
||||
fmt.Printf(" Starting services...\n")
|
||||
utils.StartServicesOrdered(services, "start")
|
||||
fmt.Printf(" Services started\n")
|
||||
|
||||
// 3. Wait for global RQLite (port 5001) to be ready
|
||||
fmt.Printf(" Waiting for global RQLite (port 5001)...\n")
|
||||
if err := waitForRQLiteReady(5001, 120*time.Second); err != nil {
|
||||
fmt.Printf(" Warning: global RQLite not ready: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" Global RQLite ready\n")
|
||||
}
|
||||
|
||||
// 4. Wait for each namespace RQLite with a global timeout of 5 minutes
|
||||
nsPorts := getNamespaceRQLitePorts()
|
||||
if len(nsPorts) > 0 {
|
||||
fmt.Printf(" Waiting for %d namespace RQLite instances...\n", len(nsPorts))
|
||||
globalDeadline := time.Now().Add(5 * time.Minute)
|
||||
|
||||
healthy := 0
|
||||
failed := 0
|
||||
for ns, port := range nsPorts {
|
||||
remaining := time.Until(globalDeadline)
|
||||
if remaining <= 0 {
|
||||
fmt.Printf(" Warning: global timeout reached, skipping remaining namespaces\n")
|
||||
failed += len(nsPorts) - healthy - failed
|
||||
break
|
||||
}
|
||||
timeout := 90 * time.Second
|
||||
if remaining < timeout {
|
||||
timeout = remaining
|
||||
}
|
||||
fmt.Printf(" Waiting for namespace '%s' (port %d)...\n", ns, port)
|
||||
if err := waitForRQLiteReady(port, timeout); err != nil {
|
||||
fmt.Printf(" Warning: namespace '%s' RQLite not ready: %v\n", ns, err)
|
||||
failed++
|
||||
} else {
|
||||
fmt.Printf(" Namespace '%s' ready\n", ns)
|
||||
healthy++
|
||||
}
|
||||
}
|
||||
fmt.Printf(" Namespace RQLite: %d healthy, %d failed\n", healthy, failed)
|
||||
}
|
||||
|
||||
// 5. Remove maintenance flag
|
||||
if err := os.Remove(maintenanceFlagPath); err != nil && !os.IsNotExist(err) {
|
||||
fmt.Printf(" Warning: failed to remove maintenance flag: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" Maintenance flag removed\n")
|
||||
}
|
||||
|
||||
fmt.Printf("Post-upgrade complete. Node is back online.\n")
|
||||
}
|
||||
|
||||
// waitForRQLiteReady polls an RQLite instance's /status endpoint until it
|
||||
// reports Leader or Follower state, or the timeout expires.
|
||||
func waitForRQLiteReady(port int, timeout time.Duration) error {
|
||||
deadline := time.Now().Add(timeout)
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
url := fmt.Sprintf("http://localhost:%d/status", port)
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
var status struct {
|
||||
Store struct {
|
||||
Raft struct {
|
||||
State string `json:"state"`
|
||||
} `json:"raft"`
|
||||
} `json:"store"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &status); err == nil {
|
||||
state := status.Store.Raft.State
|
||||
if state == "Leader" || state == "Follower" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
return fmt.Errorf("timeout after %s waiting for RQLite on port %d", timeout, port)
|
||||
}
|
||||
132
pkg/cli/production/lifecycle/pre_upgrade.go
Normal file
132
pkg/cli/production/lifecycle/pre_upgrade.go
Normal file
@ -0,0 +1,132 @@
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/DeBrosOfficial/network/pkg/rqlite"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
maintenanceFlagPath = "/home/orama/.orama/maintenance.flag"
|
||||
)
|
||||
|
||||
// HandlePreUpgrade prepares the node for a safe rolling upgrade:
|
||||
// 1. Checks quorum safety
|
||||
// 2. Writes maintenance flag
|
||||
// 3. Transfers leadership on global RQLite (port 5001) if leader
|
||||
// 4. Transfers leadership on each namespace RQLite
|
||||
// 5. Waits 15s for metadata propagation (H5 fix)
|
||||
func HandlePreUpgrade() {
|
||||
if os.Geteuid() != 0 {
|
||||
fmt.Fprintf(os.Stderr, "Error: pre-upgrade must be run as root (use sudo)\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Pre-upgrade: preparing node for safe restart...\n")
|
||||
|
||||
// 1. Check quorum safety
|
||||
if warning := checkQuorumSafety(); warning != "" {
|
||||
fmt.Fprintf(os.Stderr, " UNSAFE: %s\n", warning)
|
||||
fmt.Fprintf(os.Stderr, " Aborting pre-upgrade. Use 'orama stop --force' to override.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf(" Quorum check passed\n")
|
||||
|
||||
// 2. Write maintenance flag
|
||||
if err := os.MkdirAll(filepath.Dir(maintenanceFlagPath), 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, " Warning: failed to create flag directory: %v\n", err)
|
||||
}
|
||||
if err := os.WriteFile(maintenanceFlagPath, []byte(time.Now().Format(time.RFC3339)), 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, " Warning: failed to write maintenance flag: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" Maintenance flag written\n")
|
||||
}
|
||||
|
||||
// 3. Transfer leadership on global RQLite (port 5001)
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
|
||||
fmt.Printf(" Checking global RQLite leadership (port 5001)...\n")
|
||||
if err := rqlite.TransferLeadership(5001, logger); err != nil {
|
||||
fmt.Printf(" Warning: global leadership transfer: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" Global RQLite leadership handled\n")
|
||||
}
|
||||
|
||||
// 4. Transfer leadership on each namespace RQLite
|
||||
nsPorts := getNamespaceRQLitePorts()
|
||||
for ns, port := range nsPorts {
|
||||
fmt.Printf(" Checking namespace '%s' RQLite leadership (port %d)...\n", ns, port)
|
||||
if err := rqlite.TransferLeadership(port, logger); err != nil {
|
||||
fmt.Printf(" Warning: namespace '%s' leadership transfer: %v\n", ns, err)
|
||||
} else {
|
||||
fmt.Printf(" Namespace '%s' RQLite leadership handled\n", ns)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Wait for metadata propagation (H5 fix: 15s, not 3s)
|
||||
// The peer exchange cycle is 30s, but we force-triggered metadata updates
|
||||
// via leadership transfer. 15s is sufficient for at least one exchange cycle.
|
||||
fmt.Printf(" Waiting 15s for metadata propagation...\n")
|
||||
time.Sleep(15 * time.Second)
|
||||
|
||||
fmt.Printf("Pre-upgrade complete. Node is ready for restart.\n")
|
||||
}
|
||||
|
||||
// getNamespaceRQLitePorts scans namespace env files to find RQLite HTTP ports.
|
||||
// Returns map of namespace_name → HTTP port.
|
||||
func getNamespaceRQLitePorts() map[string]int {
|
||||
namespacesDir := "/home/orama/.orama/data/namespaces"
|
||||
ports := make(map[string]int)
|
||||
|
||||
entries, err := os.ReadDir(namespacesDir)
|
||||
if err != nil {
|
||||
return ports
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
ns := entry.Name()
|
||||
envFile := filepath.Join(namespacesDir, ns, "rqlite.env")
|
||||
port := parseHTTPPortFromEnv(envFile)
|
||||
if port > 0 {
|
||||
ports[ns] = port
|
||||
}
|
||||
}
|
||||
|
||||
return ports
|
||||
}
|
||||
|
||||
// parseHTTPPortFromEnv reads an env file and extracts the HTTP port from
|
||||
// the HTTP_ADDR=0.0.0.0:PORT line.
|
||||
func parseHTTPPortFromEnv(envFile string) int {
|
||||
f, err := os.Open(envFile)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(line, "HTTP_ADDR=") {
|
||||
addr := strings.TrimPrefix(line, "HTTP_ADDR=")
|
||||
// Format: 0.0.0.0:PORT
|
||||
if idx := strings.LastIndex(addr, ":"); idx >= 0 {
|
||||
if port, err := strconv.Atoi(addr[idx+1:]); err == nil {
|
||||
return port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
145
pkg/cli/production/lifecycle/quorum.go
Normal file
145
pkg/cli/production/lifecycle/quorum.go
Normal file
@ -0,0 +1,145 @@
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// checkQuorumSafety queries local RQLite to determine if stopping this node
|
||||
// would break quorum. Returns a warning message if unsafe, empty string if safe.
|
||||
func checkQuorumSafety() string {
|
||||
// Query local RQLite status to check if we're a voter
|
||||
status, err := getLocalRQLiteStatus()
|
||||
if err != nil {
|
||||
// RQLite may not be running — safe to stop
|
||||
return ""
|
||||
}
|
||||
|
||||
raftState, _ := status["state"].(string)
|
||||
isVoter, _ := status["voter"].(bool)
|
||||
|
||||
// If we're not a voter, stopping is always safe for quorum
|
||||
if !isVoter {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Query /nodes to count reachable voters
|
||||
nodes, err := getLocalRQLiteNodes()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Cannot verify quorum safety (failed to query nodes: %v). This node is a %s voter.", err, raftState)
|
||||
}
|
||||
|
||||
reachableVoters := 0
|
||||
totalVoters := 0
|
||||
for _, node := range nodes {
|
||||
voter, _ := node["voter"].(bool)
|
||||
reachable, _ := node["reachable"].(bool)
|
||||
if voter {
|
||||
totalVoters++
|
||||
if reachable {
|
||||
reachableVoters++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After removing this voter, remaining voters must form quorum:
|
||||
// quorum = (totalVoters / 2) + 1, so we need reachableVoters - 1 >= quorum
|
||||
remainingVoters := reachableVoters - 1
|
||||
quorumNeeded := (totalVoters-1)/2 + 1
|
||||
|
||||
if remainingVoters < quorumNeeded {
|
||||
role := raftState
|
||||
if role == "Leader" {
|
||||
role = "the LEADER"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"Stopping this node (%s, %s) would break RQLite quorum (%d/%d reachable voters would remain, need %d).",
|
||||
role, "voter", remainingVoters, totalVoters-1, quorumNeeded)
|
||||
}
|
||||
|
||||
if raftState == "Leader" {
|
||||
// Not quorum-breaking but warn about leadership
|
||||
fmt.Printf(" Note: This node is the RQLite leader. Leadership will transfer on shutdown.\n")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// getLocalRQLiteStatus queries local RQLite /status and extracts raft info
|
||||
func getLocalRQLiteStatus() (map[string]interface{}, error) {
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := client.Get("http://localhost:5001/status")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status map[string]interface{}
|
||||
if err := json.Unmarshal(body, &status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract raft state from nested structure
|
||||
store, _ := status["store"].(map[string]interface{})
|
||||
if store == nil {
|
||||
return nil, fmt.Errorf("no store in status")
|
||||
}
|
||||
raft, _ := store["raft"].(map[string]interface{})
|
||||
if raft == nil {
|
||||
return nil, fmt.Errorf("no raft in status")
|
||||
}
|
||||
|
||||
// Add voter status from the node info
|
||||
result := map[string]interface{}{
|
||||
"state": raft["state"],
|
||||
"voter": true, // Local node queries /status which doesn't include voter flag, assume voter if we got here
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getLocalRQLiteNodes queries local RQLite /nodes?nonvoters to get cluster members
|
||||
func getLocalRQLiteNodes() ([]map[string]interface{}, error) {
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := client.Get("http://localhost:5001/nodes?nonvoters&timeout=3s")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RQLite /nodes returns a map of node_id -> node_info
|
||||
var nodesMap map[string]map[string]interface{}
|
||||
if err := json.Unmarshal(body, &nodesMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodes []map[string]interface{}
|
||||
for _, node := range nodesMap {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// containsService checks if a service name exists in the service list
|
||||
func containsService(services []string, name string) bool {
|
||||
for _, s := range services {
|
||||
if s == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -35,22 +35,22 @@ func HandleRestartWithFlags(force bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Restarting all DeBros production services...\n")
|
||||
fmt.Printf("Restarting all Orama production services...\n")
|
||||
|
||||
services := utils.GetProductionServices()
|
||||
if len(services) == 0 {
|
||||
fmt.Printf(" No DeBros services found\n")
|
||||
fmt.Printf(" No Orama services found\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Ordered stop: gateway first, then node (RQLite), then supporting services
|
||||
fmt.Printf("\n Stopping services (ordered)...\n")
|
||||
shutdownOrder := [][]string{
|
||||
{"debros-gateway"},
|
||||
{"debros-node"},
|
||||
{"debros-olric"},
|
||||
{"debros-ipfs-cluster", "debros-ipfs"},
|
||||
{"debros-anyone-relay", "anyone-client"},
|
||||
{"orama-gateway"},
|
||||
{"orama-node"},
|
||||
{"orama-olric"},
|
||||
{"orama-ipfs-cluster", "orama-ipfs"},
|
||||
{"orama-anyone-relay", "anyone-client"},
|
||||
{"coredns", "caddy"},
|
||||
}
|
||||
|
||||
|
||||
@ -16,11 +16,11 @@ func HandleStart() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Starting all DeBros production services...\n")
|
||||
fmt.Printf("Starting all Orama production services...\n")
|
||||
|
||||
services := utils.GetProductionServices()
|
||||
if len(services) == 0 {
|
||||
fmt.Printf(" ⚠️ No DeBros services found\n")
|
||||
fmt.Printf(" ⚠️ No Orama services found\n")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ func HandleStopWithFlags(force bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Stopping all DeBros production services...\n")
|
||||
fmt.Printf("Stopping all Orama production services...\n")
|
||||
|
||||
// First, stop all namespace services
|
||||
fmt.Printf("\n Stopping namespace services...\n")
|
||||
@ -44,7 +44,7 @@ func HandleStopWithFlags(force bool) {
|
||||
|
||||
services := utils.GetProductionServices()
|
||||
if len(services) == 0 {
|
||||
fmt.Printf(" No DeBros services found\n")
|
||||
fmt.Printf(" No Orama services found\n")
|
||||
return
|
||||
}
|
||||
|
||||
@ -53,12 +53,12 @@ func HandleStopWithFlags(force bool) {
|
||||
// Ordered shutdown: gateway first, then node (RQLite), then supporting services
|
||||
// This ensures we stop accepting requests before shutting down the database
|
||||
shutdownOrder := [][]string{
|
||||
{"debros-gateway"}, // 1. Stop accepting new requests
|
||||
{"debros-node"}, // 2. Stop node (includes RQLite with leadership transfer)
|
||||
{"debros-olric"}, // 3. Stop cache
|
||||
{"debros-ipfs-cluster", "debros-ipfs"}, // 4. Stop storage
|
||||
{"debros-anyone-relay", "anyone-client"}, // 5. Stop privacy relay
|
||||
{"coredns", "caddy"}, // 6. Stop DNS/TLS last
|
||||
{"orama-gateway"}, // 1. Stop accepting new requests
|
||||
{"orama-node"}, // 2. Stop node (includes RQLite with leadership transfer)
|
||||
{"orama-olric"}, // 3. Stop cache
|
||||
{"orama-ipfs-cluster", "orama-ipfs"}, // 4. Stop storage
|
||||
{"orama-anyone-relay", "anyone-client"}, // 5. Stop privacy relay
|
||||
{"coredns", "caddy"}, // 6. Stop DNS/TLS last
|
||||
}
|
||||
|
||||
// First, disable all services to prevent auto-restart
|
||||
@ -157,7 +157,7 @@ func HandleStopWithFlags(force bool) {
|
||||
|
||||
if hadError {
|
||||
fmt.Fprintf(os.Stderr, "\n⚠️ Some services may still be restarting due to Restart=always\n")
|
||||
fmt.Fprintf(os.Stderr, " Check status with: systemctl list-units 'debros-*'\n")
|
||||
fmt.Fprintf(os.Stderr, " Check status with: systemctl list-units 'orama-*'\n")
|
||||
fmt.Fprintf(os.Stderr, " If services are still restarting, they may need manual intervention\n")
|
||||
} else {
|
||||
fmt.Printf("\n✅ All services stopped and disabled (will not auto-start on boot)\n")
|
||||
@ -168,7 +168,7 @@ func HandleStopWithFlags(force bool) {
|
||||
// stopAllNamespaceServices stops all running namespace services
|
||||
func stopAllNamespaceServices() {
|
||||
// Find all running namespace services using systemctl list-units
|
||||
cmd := exec.Command("systemctl", "list-units", "--type=service", "--all", "--no-pager", "--no-legend", "debros-namespace-*@*.service")
|
||||
cmd := exec.Command("systemctl", "list-units", "--type=service", "--all", "--no-pager", "--no-legend", "orama-namespace-*@*.service")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf(" ⚠️ Warning: Failed to list namespace services: %v\n", err)
|
||||
@ -181,7 +181,7 @@ func stopAllNamespaceServices() {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 0 {
|
||||
serviceName := fields[0]
|
||||
if strings.HasPrefix(serviceName, "debros-namespace-") {
|
||||
if strings.HasPrefix(serviceName, "orama-namespace-") {
|
||||
namespaceServices = append(namespaceServices, serviceName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func Handle(args []string) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "\nAvailable service aliases: node, ipfs, cluster, gateway, olric\n")
|
||||
fmt.Fprintf(os.Stderr, "Or use full service name like: debros-node\n")
|
||||
fmt.Fprintf(os.Stderr, "Or use full service name like: orama-node\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ func showUsage() {
|
||||
fmt.Fprintf(os.Stderr, "\nService aliases:\n")
|
||||
fmt.Fprintf(os.Stderr, " node, ipfs, cluster, gateway, olric\n")
|
||||
fmt.Fprintf(os.Stderr, "\nOr use full service name:\n")
|
||||
fmt.Fprintf(os.Stderr, " debros-node, debros-gateway, etc.\n")
|
||||
fmt.Fprintf(os.Stderr, " orama-node, orama-gateway, etc.\n")
|
||||
}
|
||||
|
||||
func handleMultipleServices(serviceNames []string, serviceAlias string, follow bool) {
|
||||
|
||||
@ -28,7 +28,7 @@ func Handle(args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
oramaDir := "/home/debros/.orama"
|
||||
oramaDir := "/home/orama/.orama"
|
||||
|
||||
fmt.Printf("🔄 Checking for installations to migrate...\n\n")
|
||||
|
||||
@ -70,9 +70,9 @@ func Handle(args []string) {
|
||||
|
||||
func stopOldServices() {
|
||||
oldServices := []string{
|
||||
"debros-ipfs",
|
||||
"debros-ipfs-cluster",
|
||||
"debros-node",
|
||||
"orama-ipfs",
|
||||
"orama-ipfs-cluster",
|
||||
"orama-node",
|
||||
}
|
||||
|
||||
fmt.Printf("\n Stopping old services...\n")
|
||||
@ -141,9 +141,9 @@ func migrateConfigFiles(oramaDir string) {
|
||||
|
||||
func removeOldServices() {
|
||||
oldServices := []string{
|
||||
"debros-ipfs",
|
||||
"debros-ipfs-cluster",
|
||||
"debros-node",
|
||||
"orama-ipfs",
|
||||
"orama-ipfs-cluster",
|
||||
"orama-node",
|
||||
}
|
||||
|
||||
fmt.Printf("\n Removing old service files...\n")
|
||||
|
||||
@ -24,9 +24,9 @@ func (v *Validator) CheckNeedsMigration() bool {
|
||||
}
|
||||
|
||||
oldServices := []string{
|
||||
"debros-ipfs",
|
||||
"debros-ipfs-cluster",
|
||||
"debros-node",
|
||||
"orama-ipfs",
|
||||
"orama-ipfs-cluster",
|
||||
"orama-node",
|
||||
}
|
||||
|
||||
oldConfigs := []string{
|
||||
|
||||
@ -13,21 +13,21 @@ func Handle() {
|
||||
|
||||
// Unified service names (no bootstrap/node distinction)
|
||||
serviceNames := []string{
|
||||
"debros-ipfs",
|
||||
"debros-ipfs-cluster",
|
||||
"orama-ipfs",
|
||||
"orama-ipfs-cluster",
|
||||
// Note: RQLite is managed by node process, not as separate service
|
||||
"debros-olric",
|
||||
"debros-node",
|
||||
"debros-gateway",
|
||||
"orama-olric",
|
||||
"orama-node",
|
||||
"orama-gateway",
|
||||
}
|
||||
|
||||
// Friendly descriptions
|
||||
descriptions := map[string]string{
|
||||
"debros-ipfs": "IPFS Daemon",
|
||||
"debros-ipfs-cluster": "IPFS Cluster",
|
||||
"debros-olric": "Olric Cache Server",
|
||||
"debros-node": "DeBros Node (includes RQLite)",
|
||||
"debros-gateway": "DeBros Gateway",
|
||||
"orama-ipfs": "IPFS Daemon",
|
||||
"orama-ipfs-cluster": "IPFS Cluster",
|
||||
"orama-olric": "Olric Cache Server",
|
||||
"orama-node": "Orama Node (includes RQLite)",
|
||||
"orama-gateway": "Orama Gateway",
|
||||
}
|
||||
|
||||
fmt.Printf("Services:\n")
|
||||
@ -47,7 +47,7 @@ func Handle() {
|
||||
}
|
||||
|
||||
fmt.Printf("\nDirectories:\n")
|
||||
oramaDir := "/home/debros/.orama"
|
||||
oramaDir := "/home/orama/.orama"
|
||||
if _, err := os.Stat(oramaDir); err == nil {
|
||||
fmt.Printf(" ✅ %s exists\n", oramaDir)
|
||||
} else {
|
||||
|
||||
@ -16,8 +16,8 @@ func Handle() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("⚠️ This will stop and remove all DeBros production services\n")
|
||||
fmt.Printf("⚠️ Configuration and data will be preserved in /home/debros/.orama\n\n")
|
||||
fmt.Printf("⚠️ This will stop and remove all Orama production services\n")
|
||||
fmt.Printf("⚠️ Configuration and data will be preserved in /home/orama/.orama\n\n")
|
||||
fmt.Printf("Continue? (yes/no): ")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@ -30,12 +30,12 @@ func Handle() {
|
||||
}
|
||||
|
||||
services := []string{
|
||||
"debros-gateway",
|
||||
"debros-node",
|
||||
"debros-olric",
|
||||
"debros-ipfs-cluster",
|
||||
"debros-ipfs",
|
||||
"debros-anyone-client",
|
||||
"orama-gateway",
|
||||
"orama-node",
|
||||
"orama-olric",
|
||||
"orama-ipfs-cluster",
|
||||
"orama-ipfs",
|
||||
"orama-anyone-client",
|
||||
}
|
||||
|
||||
fmt.Printf("Stopping services...\n")
|
||||
@ -48,6 +48,6 @@ func Handle() {
|
||||
|
||||
exec.Command("systemctl", "daemon-reload").Run()
|
||||
fmt.Printf("✅ Services uninstalled\n")
|
||||
fmt.Printf(" Configuration and data preserved in /home/debros/.orama\n")
|
||||
fmt.Printf(" To remove all data: rm -rf /home/debros/.orama\n\n")
|
||||
fmt.Printf(" Configuration and data preserved in /home/orama/.orama\n")
|
||||
fmt.Printf(" To remove all data: rm -rf /home/orama/.orama\n\n")
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ type Orchestrator struct {
|
||||
|
||||
// NewOrchestrator creates a new upgrade orchestrator
|
||||
func NewOrchestrator(flags *Flags) *Orchestrator {
|
||||
oramaHome := "/home/debros"
|
||||
oramaHome := "/home/orama"
|
||||
oramaDir := oramaHome + "/.orama"
|
||||
|
||||
// Load existing preferences
|
||||
@ -187,7 +187,7 @@ func (o *Orchestrator) Execute() error {
|
||||
|
||||
fmt.Printf(" To apply changes, restart services:\n")
|
||||
fmt.Printf(" sudo systemctl daemon-reload\n")
|
||||
fmt.Printf(" sudo systemctl restart debros-*\n")
|
||||
fmt.Printf(" sudo systemctl restart orama-*\n")
|
||||
fmt.Printf("\n")
|
||||
|
||||
return nil
|
||||
@ -357,7 +357,7 @@ func (o *Orchestrator) stopServices() error {
|
||||
fmt.Printf("\n⏹️ Stopping all services before upgrade...\n")
|
||||
serviceController := production.NewSystemdController()
|
||||
|
||||
// First, stop all namespace services (debros-namespace-*@*.service)
|
||||
// First, stop all namespace services (orama-namespace-*@*.service)
|
||||
fmt.Printf(" Stopping namespace services...\n")
|
||||
if err := o.stopAllNamespaceServices(serviceController); err != nil {
|
||||
fmt.Printf(" ⚠️ Warning: Failed to stop namespace services: %v\n", err)
|
||||
@ -365,15 +365,15 @@ func (o *Orchestrator) stopServices() error {
|
||||
|
||||
// Stop services in reverse dependency order
|
||||
services := []string{
|
||||
"caddy.service", // Depends on node
|
||||
"coredns.service", // Depends on node
|
||||
"debros-gateway.service", // Legacy
|
||||
"debros-node.service", // Depends on cluster, olric
|
||||
"debros-ipfs-cluster.service", // Depends on IPFS
|
||||
"debros-ipfs.service", // Base IPFS
|
||||
"debros-olric.service", // Independent
|
||||
"debros-anyone-client.service", // Client mode
|
||||
"debros-anyone-relay.service", // Relay mode
|
||||
"caddy.service", // Depends on node
|
||||
"coredns.service", // Depends on node
|
||||
"orama-gateway.service", // Legacy
|
||||
"orama-node.service", // Depends on cluster, olric
|
||||
"orama-ipfs-cluster.service", // Depends on IPFS
|
||||
"orama-ipfs.service", // Base IPFS
|
||||
"orama-olric.service", // Independent
|
||||
"orama-anyone-client.service", // Client mode
|
||||
"orama-anyone-relay.service", // Relay mode
|
||||
}
|
||||
for _, svc := range services {
|
||||
unitPath := filepath.Join("/etc/systemd/system", svc)
|
||||
@ -393,7 +393,7 @@ func (o *Orchestrator) stopServices() error {
|
||||
// stopAllNamespaceServices stops all running namespace services
|
||||
func (o *Orchestrator) stopAllNamespaceServices(serviceController *production.SystemdController) error {
|
||||
// Find all running namespace services using systemctl list-units
|
||||
cmd := exec.Command("systemctl", "list-units", "--type=service", "--state=running", "--no-pager", "--no-legend", "debros-namespace-*@*.service")
|
||||
cmd := exec.Command("systemctl", "list-units", "--type=service", "--state=running", "--no-pager", "--no-legend", "orama-namespace-*@*.service")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list namespace services: %w", err)
|
||||
@ -405,7 +405,7 @@ func (o *Orchestrator) stopAllNamespaceServices(serviceController *production.Sy
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 0 {
|
||||
serviceName := fields[0]
|
||||
if strings.HasPrefix(serviceName, "debros-namespace-") {
|
||||
if strings.HasPrefix(serviceName, "orama-namespace-") {
|
||||
if err := serviceController.StopService(serviceName); err != nil {
|
||||
fmt.Printf(" ⚠️ Warning: Failed to stop %s: %v\n", serviceName, err)
|
||||
} else {
|
||||
@ -428,9 +428,9 @@ func (o *Orchestrator) installNamespaceTemplates() error {
|
||||
systemdDir := "/etc/systemd/system"
|
||||
|
||||
templates := []string{
|
||||
"debros-namespace-rqlite@.service",
|
||||
"debros-namespace-olric@.service",
|
||||
"debros-namespace-gateway@.service",
|
||||
"orama-namespace-rqlite@.service",
|
||||
"orama-namespace-olric@.service",
|
||||
"orama-namespace-gateway@.service",
|
||||
}
|
||||
|
||||
installedCount := 0
|
||||
@ -636,7 +636,7 @@ func (o *Orchestrator) restartServices() error {
|
||||
services := utils.GetProductionServices()
|
||||
|
||||
// Re-enable all services BEFORE restarting them.
|
||||
// orama prod stop disables services, and debros-node's PartOf= dependency
|
||||
// orama prod stop disables services, and orama-node's PartOf= dependency
|
||||
// won't propagate restart to disabled services. We must re-enable first
|
||||
// so that all services restart with the updated binary.
|
||||
for _, svc := range services {
|
||||
@ -664,13 +664,13 @@ func (o *Orchestrator) restartServices() error {
|
||||
// Define the order for rolling restart - node service first (contains RQLite)
|
||||
// This ensures the cluster can reform before other services start
|
||||
priorityOrder := []string{
|
||||
"debros-node", // Start node first - contains RQLite cluster
|
||||
"debros-olric", // Distributed cache
|
||||
"debros-ipfs", // IPFS daemon
|
||||
"debros-ipfs-cluster", // IPFS cluster
|
||||
"debros-gateway", // Gateway (legacy)
|
||||
"coredns", // DNS server
|
||||
"caddy", // Reverse proxy
|
||||
"orama-node", // Start node first - contains RQLite cluster
|
||||
"orama-olric", // Distributed cache
|
||||
"orama-ipfs", // IPFS daemon
|
||||
"orama-ipfs-cluster", // IPFS cluster
|
||||
"orama-gateway", // Gateway (legacy)
|
||||
"coredns", // DNS server
|
||||
"caddy", // Reverse proxy
|
||||
}
|
||||
|
||||
// Restart services in priority order with health checks
|
||||
@ -685,7 +685,7 @@ func (o *Orchestrator) restartServices() error {
|
||||
fmt.Printf(" ✓ Started %s\n", svc)
|
||||
|
||||
// For the node service, wait for RQLite cluster health
|
||||
if svc == "debros-node" {
|
||||
if svc == "orama-node" {
|
||||
fmt.Printf(" Waiting for RQLite cluster to become healthy...\n")
|
||||
if err := o.waitForClusterHealth(2 * time.Minute); err != nil {
|
||||
fmt.Printf(" ⚠️ Cluster health check warning: %v\n", err)
|
||||
@ -793,7 +793,7 @@ func (o *Orchestrator) waitForClusterHealth(timeout time.Duration) error {
|
||||
// by looking for the systemd service file or the anonrc config file.
|
||||
func detectAnyoneRelay(oramaDir string) bool {
|
||||
// Check if systemd service file exists
|
||||
if _, err := os.Stat("/etc/systemd/system/debros-anyone-relay.service"); err == nil {
|
||||
if _, err := os.Stat("/etc/systemd/system/orama-anyone-relay.service"); err == nil {
|
||||
return true
|
||||
}
|
||||
// Check if anonrc config exists
|
||||
|
||||
@ -77,7 +77,7 @@ func ShowDryRunSummaryWithRelay(vpsIP, domain, branch string, peers []string, jo
|
||||
} else {
|
||||
fmt.Printf(" - anyone-client (npm)\n")
|
||||
}
|
||||
fmt.Printf(" - DeBros binaries (built from %s branch)\n", branch)
|
||||
fmt.Printf(" - Orama binaries (built from %s branch)\n", branch)
|
||||
|
||||
fmt.Printf("\n🔐 Secrets that would be generated:\n")
|
||||
fmt.Printf(" - Cluster secret (64-hex)\n")
|
||||
@ -89,14 +89,14 @@ func ShowDryRunSummaryWithRelay(vpsIP, domain, branch string, peers []string, jo
|
||||
fmt.Printf(" - %s/configs/olric/config.yaml\n", oramaDir)
|
||||
|
||||
fmt.Printf("\n⚙️ Systemd services that would be created:\n")
|
||||
fmt.Printf(" - debros-ipfs.service\n")
|
||||
fmt.Printf(" - debros-ipfs-cluster.service\n")
|
||||
fmt.Printf(" - debros-olric.service\n")
|
||||
fmt.Printf(" - debros-node.service (includes embedded gateway + RQLite)\n")
|
||||
fmt.Printf(" - orama-ipfs.service\n")
|
||||
fmt.Printf(" - orama-ipfs-cluster.service\n")
|
||||
fmt.Printf(" - orama-olric.service\n")
|
||||
fmt.Printf(" - orama-node.service (includes embedded gateway + RQLite)\n")
|
||||
if relayInfo != nil && relayInfo.Enabled {
|
||||
fmt.Printf(" - debros-anyone-relay.service (relay operator mode)\n")
|
||||
fmt.Printf(" - orama-anyone-relay.service (relay operator mode)\n")
|
||||
} else {
|
||||
fmt.Printf(" - debros-anyone-client.service\n")
|
||||
fmt.Printf(" - orama-anyone-client.service\n")
|
||||
}
|
||||
|
||||
fmt.Printf("\n🌐 Ports that would be used:\n")
|
||||
|
||||
@ -23,23 +23,23 @@ type PortSpec struct {
|
||||
}
|
||||
|
||||
var ServicePorts = map[string][]PortSpec{
|
||||
"debros-gateway": {
|
||||
"orama-gateway": {
|
||||
{Name: "Gateway API", Port: constants.GatewayAPIPort},
|
||||
},
|
||||
"debros-olric": {
|
||||
"orama-olric": {
|
||||
{Name: "Olric HTTP", Port: constants.OlricHTTPPort},
|
||||
{Name: "Olric Memberlist", Port: constants.OlricMemberlistPort},
|
||||
},
|
||||
"debros-node": {
|
||||
"orama-node": {
|
||||
{Name: "RQLite HTTP", Port: constants.RQLiteHTTPPort},
|
||||
{Name: "RQLite Raft", Port: constants.RQLiteRaftPort},
|
||||
},
|
||||
"debros-ipfs": {
|
||||
"orama-ipfs": {
|
||||
{Name: "IPFS API", Port: 4501},
|
||||
{Name: "IPFS Gateway", Port: 8080},
|
||||
{Name: "IPFS Swarm", Port: 4101},
|
||||
},
|
||||
"debros-ipfs-cluster": {
|
||||
"orama-ipfs-cluster": {
|
||||
{Name: "IPFS Cluster API", Port: 9094},
|
||||
},
|
||||
}
|
||||
@ -63,13 +63,13 @@ func DefaultPorts() []PortSpec {
|
||||
func ResolveServiceName(alias string) ([]string, error) {
|
||||
// Service alias mapping (unified - no bootstrap/node distinction)
|
||||
aliases := map[string][]string{
|
||||
"node": {"debros-node"},
|
||||
"ipfs": {"debros-ipfs"},
|
||||
"cluster": {"debros-ipfs-cluster"},
|
||||
"ipfs-cluster": {"debros-ipfs-cluster"},
|
||||
"gateway": {"debros-gateway"},
|
||||
"olric": {"debros-olric"},
|
||||
"rqlite": {"debros-node"}, // RQLite logs are in node logs
|
||||
"node": {"orama-node"},
|
||||
"ipfs": {"orama-ipfs"},
|
||||
"cluster": {"orama-ipfs-cluster"},
|
||||
"ipfs-cluster": {"orama-ipfs-cluster"},
|
||||
"gateway": {"orama-gateway"},
|
||||
"olric": {"orama-olric"},
|
||||
"rqlite": {"orama-node"}, // RQLite logs are in node logs
|
||||
}
|
||||
|
||||
// Check if it's an alias
|
||||
@ -153,18 +153,18 @@ func IsServiceMasked(service string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GetProductionServices returns a list of all DeBros production service names that exist,
|
||||
// GetProductionServices returns a list of all Orama production service names that exist,
|
||||
// including both global services and namespace-specific services
|
||||
func GetProductionServices() []string {
|
||||
// Global/default service names
|
||||
globalServices := []string{
|
||||
"debros-gateway",
|
||||
"debros-node",
|
||||
"debros-olric",
|
||||
"debros-ipfs-cluster",
|
||||
"debros-ipfs",
|
||||
"debros-anyone-client",
|
||||
"debros-anyone-relay",
|
||||
"orama-gateway",
|
||||
"orama-node",
|
||||
"orama-olric",
|
||||
"orama-ipfs-cluster",
|
||||
"orama-ipfs",
|
||||
"orama-anyone-client",
|
||||
"orama-anyone-relay",
|
||||
}
|
||||
|
||||
var existing []string
|
||||
@ -179,10 +179,10 @@ func GetProductionServices() []string {
|
||||
|
||||
// Discover namespace service instances from the namespaces data directory.
|
||||
// We can't rely on scanning /etc/systemd/system because that only contains
|
||||
// template files (e.g. debros-namespace-gateway@.service) with no instance name.
|
||||
// template files (e.g. orama-namespace-gateway@.service) with no instance name.
|
||||
// Restarting a template without an instance is a no-op.
|
||||
// Instead, scan the data directory where each subdirectory is a provisioned namespace.
|
||||
namespacesDir := "/home/debros/.orama/data/namespaces"
|
||||
namespacesDir := "/home/orama/.orama/data/namespaces"
|
||||
nsEntries, err := os.ReadDir(namespacesDir)
|
||||
if err == nil {
|
||||
serviceTypes := []string{"rqlite", "olric", "gateway"}
|
||||
@ -195,7 +195,7 @@ func GetProductionServices() []string {
|
||||
// Only add if the env file exists (service was provisioned)
|
||||
envFile := filepath.Join(namespacesDir, ns, svcType+".env")
|
||||
if _, err := os.Stat(envFile); err == nil {
|
||||
svcName := fmt.Sprintf("debros-namespace-%s@%s", svcType, ns)
|
||||
svcName := fmt.Sprintf("orama-namespace-%s@%s", svcType, ns)
|
||||
existing = append(existing, svcName)
|
||||
}
|
||||
}
|
||||
@ -304,7 +304,7 @@ func StartServicesOrdered(services []string, action string) {
|
||||
for _, svc := range services {
|
||||
matched := false
|
||||
for _, svcType := range NamespaceServiceOrder {
|
||||
prefix := "debros-namespace-" + svcType + "@"
|
||||
prefix := "orama-namespace-" + svcType + "@"
|
||||
if strings.HasPrefix(svc, prefix) {
|
||||
nsServices[svcType] = append(nsServices[svcType], svc)
|
||||
matched = true
|
||||
@ -348,4 +348,3 @@ func StartServicesOrdered(services []string, action string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
// These can be overridden by environment variables or config.
|
||||
func DefaultBootstrapPeers() []string {
|
||||
// Check environment variable first
|
||||
if envPeers := os.Getenv("DEBROS_BOOTSTRAP_PEERS"); envPeers != "" {
|
||||
if envPeers := os.Getenv("ORAMA_BOOTSTRAP_PEERS"); envPeers != "" {
|
||||
peers := splitCSVOrSpace(envPeers)
|
||||
// Filter out empty strings
|
||||
result := make([]string, 0, len(peers))
|
||||
|
||||
@ -8,11 +8,11 @@ import (
|
||||
)
|
||||
|
||||
func TestDefaultBootstrapPeersNonEmpty(t *testing.T) {
|
||||
old := os.Getenv("DEBROS_BOOTSTRAP_PEERS")
|
||||
t.Cleanup(func() { os.Setenv("DEBROS_BOOTSTRAP_PEERS", old) })
|
||||
old := os.Getenv("ORAMA_BOOTSTRAP_PEERS")
|
||||
t.Cleanup(func() { os.Setenv("ORAMA_BOOTSTRAP_PEERS", old) })
|
||||
// Set a valid peer
|
||||
validPeer := "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWHbcFcrGPXKUrHcxvd8MXEeUzRYyvY8fQcpEBxncSUwhj"
|
||||
_ = os.Setenv("DEBROS_BOOTSTRAP_PEERS", validPeer)
|
||||
_ = os.Setenv("ORAMA_BOOTSTRAP_PEERS", validPeer)
|
||||
peers := DefaultBootstrapPeers()
|
||||
if len(peers) == 0 {
|
||||
t.Fatalf("expected non-empty default peers")
|
||||
|
||||
@ -27,7 +27,7 @@ type DatabaseConfig struct {
|
||||
RaftElectionTimeout time.Duration `yaml:"raft_election_timeout"` // default: 5s
|
||||
RaftHeartbeatTimeout time.Duration `yaml:"raft_heartbeat_timeout"` // default: 2s
|
||||
RaftApplyTimeout time.Duration `yaml:"raft_apply_timeout"` // default: 30s
|
||||
RaftLeaderLeaseTimeout time.Duration `yaml:"raft_leader_lease_timeout"` // default: 5s
|
||||
RaftLeaderLeaseTimeout time.Duration `yaml:"raft_leader_lease_timeout"` // default: 2s (must be <= heartbeat timeout)
|
||||
|
||||
// Dynamic discovery configuration (always enabled)
|
||||
ClusterSyncInterval time.Duration `yaml:"cluster_sync_interval"` // default: 30s
|
||||
|
||||
@ -20,7 +20,7 @@ func ExpandPath(path string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// ConfigDir returns the path to the DeBros config directory (~/.orama).
|
||||
// ConfigDir returns the path to the Orama config directory (~/.orama).
|
||||
func ConfigDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
|
||||
@ -119,7 +119,7 @@ sudo chmod +x /usr/local/bin/coredns
|
||||
# 2. Create directories
|
||||
sudo mkdir -p /etc/coredns
|
||||
sudo mkdir -p /var/lib/coredns
|
||||
sudo chown debros:debros /var/lib/coredns
|
||||
sudo chown orama:orama /var/lib/coredns
|
||||
|
||||
# 3. Copy configuration
|
||||
sudo cp configs/coredns/Corefile /etc/coredns/
|
||||
@ -425,8 +425,8 @@ Adjust in `client.go` if needed for higher load.
|
||||
```bash
|
||||
# Increase file descriptor limit
|
||||
# Add to /etc/security/limits.conf:
|
||||
debros soft nofile 65536
|
||||
debros hard nofile 65536
|
||||
orama soft nofile 65536
|
||||
orama hard nofile 65536
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
@ -20,7 +20,7 @@ import (
|
||||
|
||||
// Manager manages deployment processes via systemd (Linux) or direct process spawning (macOS/other)
|
||||
type Manager struct {
|
||||
logger *zap.Logger
|
||||
logger *zap.Logger
|
||||
useSystemd bool
|
||||
|
||||
// For non-systemd mode: track running processes
|
||||
@ -310,8 +310,8 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory={{.WorkDir}}
|
||||
|
||||
{{range .Env}}Environment="{{.}}"
|
||||
@ -373,7 +373,7 @@ WantedBy=multi-user.target
|
||||
return err
|
||||
}
|
||||
|
||||
// Use sudo tee to write to systemd directory (debros user needs sudo access)
|
||||
// Use sudo tee to write to systemd directory (orama user needs sudo access)
|
||||
cmd := exec.Command("sudo", "tee", serviceFile)
|
||||
cmd.Stdin = &buf
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
@ -91,7 +91,7 @@ func TestGetStartCommand(t *testing.T) {
|
||||
// On macOS (test environment), useSystemd will be false, so node/npm use short paths.
|
||||
// We explicitly set it to test both modes.
|
||||
|
||||
workDir := "/home/debros/deployments/alice/myapp"
|
||||
workDir := "/home/orama/deployments/alice/myapp"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -227,9 +227,9 @@ func TestMapRestartPolicy(t *testing.T) {
|
||||
|
||||
func TestParseSystemctlShow(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want map[string]string
|
||||
name string
|
||||
input string
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
name: "typical output",
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// Protocol ID for peer exchange
|
||||
const PeerExchangeProtocol = "/debros/peer-exchange/1.0.0"
|
||||
const PeerExchangeProtocol = "/orama/peer-exchange/1.0.0"
|
||||
|
||||
// libp2pPort is the standard port used for libp2p peer connections.
|
||||
// Filtering on this port prevents cross-connecting with IPFS (4101) or IPFS Cluster (9096/9098).
|
||||
|
||||
@ -307,14 +307,14 @@ func ensureSecretFilePermissions(secretPath string) error {
|
||||
return fmt.Errorf("failed to set permissions on %s: %w", secretPath, err)
|
||||
}
|
||||
|
||||
if usr, err := user.Lookup("debros"); err == nil {
|
||||
if usr, err := user.Lookup("orama"); err == nil {
|
||||
uid, err := strconv.Atoi(usr.Uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse debros UID: %w", err)
|
||||
return fmt.Errorf("failed to parse orama UID: %w", err)
|
||||
}
|
||||
gid, err := strconv.Atoi(usr.Gid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse debros GID: %w", err)
|
||||
return fmt.Errorf("failed to parse orama GID: %w", err)
|
||||
}
|
||||
if err := os.Chown(secretPath, uid, gid); err != nil {
|
||||
return fmt.Errorf("failed to change ownership of %s: %w", secretPath, err)
|
||||
@ -439,8 +439,8 @@ func (sg *SecretGenerator) SaveConfig(filename string, content string) error {
|
||||
}
|
||||
|
||||
// Fix ownership
|
||||
if err := exec.Command("chown", "debros:debros", configPath).Run(); err != nil {
|
||||
fmt.Printf("Warning: failed to chown %s to debros:debros: %v\n", configPath, err)
|
||||
if err := exec.Command("chown", "orama:orama", configPath).Run(); err != nil {
|
||||
fmt.Printf("Warning: failed to chown %s to orama:orama: %v\n", configPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -27,7 +27,7 @@ type BinaryInstaller struct {
|
||||
|
||||
// NewBinaryInstaller creates a new binary installer
|
||||
func NewBinaryInstaller(arch string, logWriter io.Writer) *BinaryInstaller {
|
||||
oramaHome := "/home/debros"
|
||||
oramaHome := "/home/orama"
|
||||
return &BinaryInstaller{
|
||||
arch: arch,
|
||||
logWriter: logWriter,
|
||||
@ -72,7 +72,7 @@ func (bi *BinaryInstaller) ResolveBinaryPath(binary string, extraPaths ...string
|
||||
return installers.ResolveBinaryPath(binary, extraPaths...)
|
||||
}
|
||||
|
||||
// InstallDeBrosBinaries builds DeBros binaries from source
|
||||
// InstallDeBrosBinaries builds Orama binaries from source
|
||||
func (bi *BinaryInstaller) InstallDeBrosBinaries(oramaHome string) error {
|
||||
return bi.gateway.InstallDeBrosBinaries(oramaHome)
|
||||
}
|
||||
|
||||
@ -194,7 +194,7 @@ func (ari *AnyoneRelayInstaller) Install() error {
|
||||
os.Remove(installScript)
|
||||
|
||||
// Stop and disable the default 'anon' systemd service that the apt package
|
||||
// auto-enables. We use our own 'debros-anyone-relay' service instead.
|
||||
// auto-enables. We use our own 'orama-anyone-relay' service instead.
|
||||
exec.Command("systemctl", "stop", "anon").Run()
|
||||
exec.Command("systemctl", "disable", "anon").Run()
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ func (ci *CaddyInstaller) Install() error {
|
||||
if _, err := exec.LookPath("xcaddy"); err != nil {
|
||||
fmt.Fprintf(ci.logWriter, " Installing xcaddy...\n")
|
||||
cmd := exec.Command("go", "install", xcaddyRepo)
|
||||
cmd.Env = append(os.Environ(), "PATH="+goPath, "GOBIN=/usr/local/bin")
|
||||
cmd.Env = append(os.Environ(), "PATH="+goPath, "GOBIN=/usr/local/bin", "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to install xcaddy: %w\n%s", err, string(output))
|
||||
}
|
||||
@ -105,7 +105,7 @@ func (ci *CaddyInstaller) Install() error {
|
||||
// Run go mod tidy
|
||||
tidyCmd := exec.Command("go", "mod", "tidy")
|
||||
tidyCmd.Dir = moduleDir
|
||||
tidyCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
tidyCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := tidyCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to run go mod tidy: %w\n%s", err, string(output))
|
||||
}
|
||||
@ -122,7 +122,7 @@ func (ci *CaddyInstaller) Install() error {
|
||||
"--with", "github.com/DeBrosOfficial/caddy-dns-orama="+moduleDir,
|
||||
"--output", filepath.Join(buildDir, "caddy"))
|
||||
buildCmd.Dir = buildDir
|
||||
buildCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
buildCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := buildCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to build Caddy: %w\n%s", err, string(output))
|
||||
}
|
||||
|
||||
@ -171,21 +171,21 @@ func (ci *CoreDNSInstaller) Install() error {
|
||||
|
||||
getCmd := exec.Command("go", "get", "github.com/miekg/dns@latest")
|
||||
getCmd.Dir = buildDir
|
||||
getCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
getCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := getCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to get miekg/dns: %w\n%s", err, string(output))
|
||||
}
|
||||
|
||||
getCmd = exec.Command("go", "get", "go.uber.org/zap@latest")
|
||||
getCmd.Dir = buildDir
|
||||
getCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
getCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := getCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to get zap: %w\n%s", err, string(output))
|
||||
}
|
||||
|
||||
tidyCmd := exec.Command("go", "mod", "tidy")
|
||||
tidyCmd.Dir = buildDir
|
||||
tidyCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
tidyCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := tidyCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to run go mod tidy: %w\n%s", err, string(output))
|
||||
}
|
||||
@ -194,7 +194,7 @@ func (ci *CoreDNSInstaller) Install() error {
|
||||
fmt.Fprintf(ci.logWriter, " Generating plugin code...\n")
|
||||
genCmd := exec.Command("go", "generate")
|
||||
genCmd.Dir = buildDir
|
||||
genCmd.Env = append(os.Environ(), "PATH="+goPath)
|
||||
genCmd.Env = append(os.Environ(), "PATH="+goPath, "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := genCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to generate: %w\n%s", err, string(output))
|
||||
}
|
||||
@ -203,7 +203,7 @@ func (ci *CoreDNSInstaller) Install() error {
|
||||
fmt.Fprintf(ci.logWriter, " Building CoreDNS binary...\n")
|
||||
buildCmd := exec.Command("go", "build", "-o", "coredns")
|
||||
buildCmd.Dir = buildDir
|
||||
buildCmd.Env = append(os.Environ(), "PATH="+goPath, "CGO_ENABLED=0")
|
||||
buildCmd.Env = append(os.Environ(), "PATH="+goPath, "CGO_ENABLED=0", "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := buildCmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to build CoreDNS: %w\n%s", err, string(output))
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GatewayInstaller handles DeBros binary installation (including gateway)
|
||||
// GatewayInstaller handles Orama binary installation (including gateway)
|
||||
type GatewayInstaller struct {
|
||||
*BaseInstaller
|
||||
}
|
||||
@ -27,7 +27,7 @@ func (gi *GatewayInstaller) IsInstalled() bool {
|
||||
return false // Always build to ensure latest version
|
||||
}
|
||||
|
||||
// Install clones and builds DeBros binaries
|
||||
// Install clones and builds Orama binaries
|
||||
func (gi *GatewayInstaller) Install() error {
|
||||
// This is a placeholder - actual installation is handled by InstallDeBrosBinaries
|
||||
return nil
|
||||
@ -39,10 +39,10 @@ func (gi *GatewayInstaller) Configure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallDeBrosBinaries builds DeBros binaries from source at /home/debros/src.
|
||||
// InstallDeBrosBinaries builds Orama binaries from source at /home/orama/src.
|
||||
// Source must already be present (uploaded via SCP archive).
|
||||
func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error {
|
||||
fmt.Fprintf(gi.logWriter, " Building DeBros binaries...\n")
|
||||
fmt.Fprintf(gi.logWriter, " Building Orama binaries...\n")
|
||||
|
||||
srcDir := filepath.Join(oramaHome, "src")
|
||||
binDir := filepath.Join(oramaHome, "bin")
|
||||
@ -64,7 +64,7 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error {
|
||||
fmt.Fprintf(gi.logWriter, " Building binaries...\n")
|
||||
cmd := exec.Command("make", "build")
|
||||
cmd.Dir = srcDir
|
||||
cmd.Env = append(os.Environ(), "HOME="+oramaHome, "PATH="+os.Getenv("PATH")+":/usr/local/go/bin")
|
||||
cmd.Env = append(os.Environ(), "HOME="+oramaHome, "PATH="+os.Getenv("PATH")+":/usr/local/go/bin", "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to build: %v\n%s", err, string(output))
|
||||
}
|
||||
@ -117,7 +117,7 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error {
|
||||
if err := exec.Command("chmod", "-R", "755", binDir).Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chmod bin directory: %v\n", err)
|
||||
}
|
||||
if err := exec.Command("chown", "-R", "debros:debros", binDir).Run(); err != nil {
|
||||
if err := exec.Command("chown", "-R", "orama:orama", binDir).Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown bin directory: %v\n", err)
|
||||
}
|
||||
|
||||
@ -132,26 +132,29 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(gi.logWriter, " ✓ DeBros binaries installed\n")
|
||||
fmt.Fprintf(gi.logWriter, " ✓ Orama binaries installed\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallGo downloads and installs Go toolchain
|
||||
func (gi *GatewayInstaller) InstallGo() error {
|
||||
requiredVersion := "1.22.5"
|
||||
requiredVersion := "1.24.6"
|
||||
if goPath, err := exec.LookPath("go"); err == nil {
|
||||
// Check version - upgrade if too old
|
||||
out, _ := exec.Command(goPath, "version").Output()
|
||||
if strings.Contains(string(out), "go"+requiredVersion) || strings.Contains(string(out), "go1.23") || strings.Contains(string(out), "go1.24") {
|
||||
if strings.Contains(string(out), "go"+requiredVersion) {
|
||||
fmt.Fprintf(gi.logWriter, " ✓ Go already installed (%s)\n", strings.TrimSpace(string(out)))
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(gi.logWriter, " Upgrading Go (current: %s, need >= %s)...\n", strings.TrimSpace(string(out)), requiredVersion)
|
||||
fmt.Fprintf(gi.logWriter, " Upgrading Go (current: %s, need %s)...\n", strings.TrimSpace(string(out)), requiredVersion)
|
||||
os.RemoveAll("/usr/local/go")
|
||||
} else {
|
||||
fmt.Fprintf(gi.logWriter, " Installing Go...\n")
|
||||
}
|
||||
|
||||
// Always remove old Go installation to avoid mixing versions
|
||||
os.RemoveAll("/usr/local/go")
|
||||
|
||||
goTarball := fmt.Sprintf("go%s.linux-%s.tar.gz", requiredVersion, gi.arch)
|
||||
goURL := fmt.Sprintf("https://go.dev/dl/%s", goTarball)
|
||||
|
||||
@ -214,12 +217,12 @@ func (gi *GatewayInstaller) InstallAnyoneClient() error {
|
||||
fmt.Fprintf(gi.logWriter, " Initializing NPM cache...\n")
|
||||
|
||||
// Create nested cache directories with proper permissions
|
||||
debrosHome := "/home/debros"
|
||||
oramaHome := "/home/orama"
|
||||
npmCacheDirs := []string{
|
||||
filepath.Join(debrosHome, ".npm"),
|
||||
filepath.Join(debrosHome, ".npm", "_cacache"),
|
||||
filepath.Join(debrosHome, ".npm", "_cacache", "tmp"),
|
||||
filepath.Join(debrosHome, ".npm", "_logs"),
|
||||
filepath.Join(oramaHome, ".npm"),
|
||||
filepath.Join(oramaHome, ".npm", "_cacache"),
|
||||
filepath.Join(oramaHome, ".npm", "_cacache", "tmp"),
|
||||
filepath.Join(oramaHome, ".npm", "_logs"),
|
||||
}
|
||||
|
||||
for _, dir := range npmCacheDirs {
|
||||
@ -227,8 +230,8 @@ func (gi *GatewayInstaller) InstallAnyoneClient() error {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Failed to create %s: %v\n", dir, err)
|
||||
continue
|
||||
}
|
||||
// Fix ownership to debros user (sequential to avoid race conditions)
|
||||
if err := exec.Command("chown", "debros:debros", dir).Run(); err != nil {
|
||||
// Fix ownership to orama user (sequential to avoid race conditions)
|
||||
if err := exec.Command("chown", "orama:orama", dir).Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown %s: %v\n", dir, err)
|
||||
}
|
||||
if err := exec.Command("chmod", "700", dir).Run(); err != nil {
|
||||
@ -236,14 +239,14 @@ func (gi *GatewayInstaller) InstallAnyoneClient() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively fix ownership of entire .npm directory to ensure all nested files are owned by debros
|
||||
if err := exec.Command("chown", "-R", "debros:debros", filepath.Join(debrosHome, ".npm")).Run(); err != nil {
|
||||
// Recursively fix ownership of entire .npm directory to ensure all nested files are owned by orama
|
||||
if err := exec.Command("chown", "-R", "orama:orama", filepath.Join(oramaHome, ".npm")).Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown .npm directory: %v\n", err)
|
||||
}
|
||||
|
||||
// Run npm cache verify as debros user with proper environment
|
||||
cacheInitCmd := exec.Command("sudo", "-u", "debros", "npm", "cache", "verify", "--silent")
|
||||
cacheInitCmd.Env = append(os.Environ(), "HOME="+debrosHome)
|
||||
// Run npm cache verify as orama user with proper environment
|
||||
cacheInitCmd := exec.Command("sudo", "-u", "orama", "npm", "cache", "verify", "--silent")
|
||||
cacheInitCmd.Env = append(os.Environ(), "HOME="+oramaHome)
|
||||
if err := cacheInitCmd.Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ NPM cache verify warning: %v (continuing anyway)\n", err)
|
||||
}
|
||||
@ -255,11 +258,11 @@ func (gi *GatewayInstaller) InstallAnyoneClient() error {
|
||||
}
|
||||
|
||||
// Create terms-agreement file to bypass interactive prompt when running as a service
|
||||
termsFile := filepath.Join(debrosHome, "terms-agreement")
|
||||
termsFile := filepath.Join(oramaHome, "terms-agreement")
|
||||
if err := os.WriteFile(termsFile, []byte("agreed"), 0644); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to create terms-agreement: %v\n", err)
|
||||
} else {
|
||||
if err := exec.Command("chown", "debros:debros", termsFile).Run(); err != nil {
|
||||
if err := exec.Command("chown", "orama:orama", termsFile).Run(); err != nil {
|
||||
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown terms-agreement: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ func (ii *IPFSInstaller) InitializeRepo(ipfsRepoPath string, swarmKeyPath string
|
||||
}
|
||||
|
||||
// Fix ownership (best-effort, don't fail if it doesn't work)
|
||||
if err := exec.Command("chown", "-R", "debros:debros", ipfsRepoPath).Run(); err != nil {
|
||||
if err := exec.Command("chown", "-R", "orama:orama", ipfsRepoPath).Run(); err != nil {
|
||||
fmt.Fprintf(ii.logWriter, " ⚠️ Warning: failed to chown IPFS repo: %v\n", err)
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ func (ici *IPFSClusterInstaller) Install() error {
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "install", "github.com/ipfs-cluster/ipfs-cluster/cmd/ipfs-cluster-service@latest")
|
||||
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin")
|
||||
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin", "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to install IPFS Cluster: %w", err)
|
||||
}
|
||||
@ -77,7 +77,7 @@ func (ici *IPFSClusterInstaller) InitializeConfig(clusterPath, clusterSecret str
|
||||
}
|
||||
|
||||
// Fix ownership before running init (best-effort)
|
||||
if err := exec.Command("chown", "-R", "debros:debros", clusterPath).Run(); err != nil {
|
||||
if err := exec.Command("chown", "-R", "orama:orama", clusterPath).Run(); err != nil {
|
||||
fmt.Fprintf(ici.logWriter, " ⚠️ Warning: failed to chown cluster path before init: %v\n", err)
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ func (ici *IPFSClusterInstaller) InitializeConfig(clusterPath, clusterSecret str
|
||||
}
|
||||
|
||||
// Fix ownership again after updates (best-effort)
|
||||
if err := exec.Command("chown", "-R", "debros:debros", clusterPath).Run(); err != nil {
|
||||
if err := exec.Command("chown", "-R", "orama:orama", clusterPath).Run(); err != nil {
|
||||
fmt.Fprintf(ici.logWriter, " ⚠️ Warning: failed to chown cluster path after updates: %v\n", err)
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ func (oi *OlricInstaller) Install() error {
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "install", fmt.Sprintf("github.com/olric-data/olric/cmd/olric-server@%s", oi.version))
|
||||
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin")
|
||||
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin", "GOPROXY=https://proxy.golang.org|direct", "GONOSUMDB=*")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to install Olric: %w", err)
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ func (ri *RQLiteInstaller) InitializeDataDir(dataDir string) error {
|
||||
return fmt.Errorf("failed to create RQLite data directory: %w", err)
|
||||
}
|
||||
|
||||
if err := exec.Command("chown", "-R", "debros:debros", dataDir).Run(); err != nil {
|
||||
if err := exec.Command("chown", "-R", "orama:orama", dataDir).Run(); err != nil {
|
||||
fmt.Fprintf(ri.logWriter, " ⚠️ Warning: failed to chown RQLite data dir: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -30,13 +30,13 @@ type AnyoneRelayConfig struct {
|
||||
type ProductionSetup struct {
|
||||
osInfo *OSInfo
|
||||
arch string
|
||||
oramaHome string
|
||||
oramaDir string
|
||||
oramaHome string
|
||||
oramaDir string
|
||||
logWriter io.Writer
|
||||
forceReconfigure bool
|
||||
skipOptionalDeps bool
|
||||
skipResourceChecks bool
|
||||
isNameserver bool // Whether this node is a nameserver (runs CoreDNS + Caddy)
|
||||
isNameserver bool // Whether this node is a nameserver (runs CoreDNS + Caddy)
|
||||
isAnyoneClient bool // Whether this node runs Anyone as client-only (SOCKS5 proxy)
|
||||
anyoneRelayConfig *AnyoneRelayConfig // Configuration for Anyone relay mode
|
||||
privChecker *PrivilegeChecker
|
||||
@ -73,12 +73,12 @@ func ReadBranchPreference(oramaDir string) string {
|
||||
func SaveBranchPreference(oramaDir, branch string) error {
|
||||
branchFile := filepath.Join(oramaDir, ".branch")
|
||||
if err := os.MkdirAll(oramaDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create debros directory: %w", err)
|
||||
return fmt.Errorf("failed to create orama directory: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(branchFile, []byte(branch), 0644); err != nil {
|
||||
return fmt.Errorf("failed to save branch preference: %w", err)
|
||||
}
|
||||
exec.Command("chown", "debros:debros", branchFile).Run()
|
||||
exec.Command("chown", "orama:orama", branchFile).Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -88,8 +88,8 @@ func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure
|
||||
arch, _ := (&ArchitectureDetector{}).Detect()
|
||||
|
||||
return &ProductionSetup{
|
||||
oramaHome: oramaHome,
|
||||
oramaDir: oramaDir,
|
||||
oramaHome: oramaHome,
|
||||
oramaDir: oramaDir,
|
||||
logWriter: logWriter,
|
||||
forceReconfigure: forceReconfigure,
|
||||
arch: arch,
|
||||
@ -100,7 +100,7 @@ func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure
|
||||
resourceChecker: NewResourceChecker(),
|
||||
portChecker: NewPortChecker(),
|
||||
fsProvisioner: NewFilesystemProvisioner(oramaHome),
|
||||
userProvisioner: NewUserProvisioner("debros", oramaHome, "/bin/bash"),
|
||||
userProvisioner: NewUserProvisioner("orama", oramaHome, "/bin/bash"),
|
||||
stateDetector: NewStateDetector(oramaDir),
|
||||
configGenerator: NewConfigGenerator(oramaDir),
|
||||
secretGenerator: NewSecretGenerator(oramaDir),
|
||||
@ -231,14 +231,14 @@ func (ps *ProductionSetup) Phase1CheckPrerequisites() error {
|
||||
func (ps *ProductionSetup) Phase2ProvisionEnvironment() error {
|
||||
ps.logf("Phase 2: Provisioning environment...")
|
||||
|
||||
// Create debros user
|
||||
// Create orama user
|
||||
if !ps.userProvisioner.UserExists() {
|
||||
if err := ps.userProvisioner.CreateUser(); err != nil {
|
||||
return fmt.Errorf("failed to create debros user: %w", err)
|
||||
return fmt.Errorf("failed to create orama user: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ Created 'debros' user")
|
||||
ps.logf(" ✓ Created 'orama' user")
|
||||
} else {
|
||||
ps.logf(" ✓ 'debros' user already exists")
|
||||
ps.logf(" ✓ 'orama' user already exists")
|
||||
}
|
||||
|
||||
// Set up sudoers access if invoked via sudo
|
||||
@ -251,21 +251,21 @@ func (ps *ProductionSetup) Phase2ProvisionEnvironment() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Set up deployment sudoers (allows debros user to manage orama-deploy-* services)
|
||||
// Set up deployment sudoers (allows orama user to manage orama-deploy-* services)
|
||||
if err := ps.userProvisioner.SetupDeploymentSudoers(); err != nil {
|
||||
ps.logf(" ⚠️ Failed to setup deployment sudoers: %v", err)
|
||||
} else {
|
||||
ps.logf(" ✓ Deployment sudoers configured")
|
||||
}
|
||||
|
||||
// Set up namespace sudoers (allows debros user to manage debros-namespace-* services)
|
||||
// Set up namespace sudoers (allows orama user to manage orama-namespace-* services)
|
||||
if err := ps.userProvisioner.SetupNamespaceSudoers(); err != nil {
|
||||
ps.logf(" ⚠️ Failed to setup namespace sudoers: %v", err)
|
||||
} else {
|
||||
ps.logf(" ✓ Namespace sudoers configured")
|
||||
}
|
||||
|
||||
// Set up WireGuard sudoers (allows debros user to manage WG peers)
|
||||
// Set up WireGuard sudoers (allows orama user to manage WG peers)
|
||||
if err := ps.userProvisioner.SetupWireGuardSudoers(); err != nil {
|
||||
ps.logf(" ⚠️ Failed to setup wireguard sudoers: %v", err)
|
||||
} else {
|
||||
@ -287,7 +287,7 @@ func (ps *ProductionSetup) Phase2ProvisionEnvironment() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Phase2bInstallBinaries installs external binaries and DeBros components
|
||||
// Phase2bInstallBinaries installs external binaries and Orama components
|
||||
func (ps *ProductionSetup) Phase2bInstallBinaries() error {
|
||||
ps.logf("Phase 2b: Installing binaries...")
|
||||
|
||||
@ -305,9 +305,9 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error {
|
||||
ps.logf(" ⚠️ Olric install warning: %v", err)
|
||||
}
|
||||
|
||||
// Install DeBros binaries (source must be at /home/debros/src via SCP)
|
||||
// Install Orama binaries (source must be at /home/orama/src via SCP)
|
||||
if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.oramaHome); err != nil {
|
||||
return fmt.Errorf("failed to install DeBros binaries: %w", err)
|
||||
return fmt.Errorf("failed to install Orama binaries: %w", err)
|
||||
}
|
||||
|
||||
// Install CoreDNS only for nameserver nodes
|
||||
@ -471,7 +471,7 @@ func (ps *ProductionSetup) Phase2cInitializeServices(peerAddresses []string, vps
|
||||
}
|
||||
|
||||
// Ensure all directories and files created during service initialization have correct ownership
|
||||
// This is critical because directories/files created as root need to be owned by debros user
|
||||
// This is critical because directories/files created as root need to be owned by orama user
|
||||
if err := ps.fsProvisioner.FixOwnership(); err != nil {
|
||||
return fmt.Errorf("failed to fix ownership after service initialization: %w", err)
|
||||
}
|
||||
@ -564,7 +564,7 @@ func (ps *ProductionSetup) Phase4GenerateConfigs(peerAddresses []string, vpsIP s
|
||||
if err := os.WriteFile(olricConfigPath, []byte(olricConfig), 0644); err != nil {
|
||||
return fmt.Errorf("failed to save olric config: %w", err)
|
||||
}
|
||||
exec.Command("chown", "debros:debros", olricConfigPath).Run()
|
||||
exec.Command("chown", "orama:orama", olricConfigPath).Run()
|
||||
ps.logf(" ✓ Olric config generated")
|
||||
|
||||
// Configure CoreDNS (if baseDomain is provided - this is the zone name)
|
||||
@ -633,44 +633,44 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
|
||||
// IPFS service (unified - no bootstrap/node distinction)
|
||||
ipfsUnit := ps.serviceGenerator.GenerateIPFSService(ipfsBinary)
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-ipfs.service", ipfsUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-ipfs.service", ipfsUnit); err != nil {
|
||||
return fmt.Errorf("failed to write IPFS service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ IPFS service created: debros-ipfs.service")
|
||||
ps.logf(" ✓ IPFS service created: orama-ipfs.service")
|
||||
|
||||
// IPFS Cluster service
|
||||
clusterUnit := ps.serviceGenerator.GenerateIPFSClusterService(clusterBinary)
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-ipfs-cluster.service", clusterUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-ipfs-cluster.service", clusterUnit); err != nil {
|
||||
return fmt.Errorf("failed to write IPFS Cluster service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ IPFS Cluster service created: debros-ipfs-cluster.service")
|
||||
ps.logf(" ✓ IPFS Cluster service created: orama-ipfs-cluster.service")
|
||||
|
||||
// RQLite is managed internally by each node - no separate systemd service needed
|
||||
|
||||
// Olric service
|
||||
olricUnit := ps.serviceGenerator.GenerateOlricService(olricBinary)
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-olric.service", olricUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-olric.service", olricUnit); err != nil {
|
||||
return fmt.Errorf("failed to write Olric service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ Olric service created")
|
||||
|
||||
// Node service (unified - includes embedded gateway)
|
||||
nodeUnit := ps.serviceGenerator.GenerateNodeService()
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-node.service", nodeUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-node.service", nodeUnit); err != nil {
|
||||
return fmt.Errorf("failed to write Node service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ Node service created: debros-node.service (with embedded gateway)")
|
||||
ps.logf(" ✓ Node service created: orama-node.service (with embedded gateway)")
|
||||
|
||||
// Anyone Relay service (only created when --anyone-relay flag is used)
|
||||
if ps.IsAnyoneRelay() {
|
||||
anyoneUnit := ps.serviceGenerator.GenerateAnyoneRelayService()
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-anyone-relay.service", anyoneUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-anyone-relay.service", anyoneUnit); err != nil {
|
||||
return fmt.Errorf("failed to write Anyone Relay service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ Anyone Relay service created (operator mode, ORPort: %d)", ps.anyoneRelayConfig.ORPort)
|
||||
} else if ps.IsAnyoneClient() {
|
||||
anyoneUnit := ps.serviceGenerator.GenerateAnyoneRelayService()
|
||||
if err := ps.serviceController.WriteServiceUnit("debros-anyone-relay.service", anyoneUnit); err != nil {
|
||||
if err := ps.serviceController.WriteServiceUnit("orama-anyone-relay.service", anyoneUnit); err != nil {
|
||||
return fmt.Errorf("failed to write Anyone client service: %w", err)
|
||||
}
|
||||
ps.logf(" ✓ Anyone client service created (SocksPort 9050)")
|
||||
@ -712,13 +712,13 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
ps.logf(" ✓ Systemd daemon reloaded")
|
||||
|
||||
// Enable services (unified names - no bootstrap/node distinction)
|
||||
// Note: debros-gateway.service is no longer needed - each node has an embedded gateway
|
||||
// Note: debros-rqlite.service is NOT created - RQLite is managed by each node internally
|
||||
services := []string{"debros-ipfs.service", "debros-ipfs-cluster.service", "debros-olric.service", "debros-node.service"}
|
||||
// Note: orama-gateway.service is no longer needed - each node has an embedded gateway
|
||||
// Note: orama-rqlite.service is NOT created - RQLite is managed by each node internally
|
||||
services := []string{"orama-ipfs.service", "orama-ipfs-cluster.service", "orama-olric.service", "orama-node.service"}
|
||||
|
||||
// Add Anyone service if configured (relay or client)
|
||||
if ps.IsAnyoneRelay() || ps.IsAnyoneClient() {
|
||||
services = append(services, "debros-anyone-relay.service")
|
||||
services = append(services, "orama-anyone-relay.service")
|
||||
}
|
||||
|
||||
// Add CoreDNS only for nameserver nodes
|
||||
@ -744,7 +744,7 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
ps.logf(" Starting services...")
|
||||
|
||||
// Start infrastructure first (IPFS, Olric, Anyone) - RQLite is managed internally by each node
|
||||
infraServices := []string{"debros-ipfs.service", "debros-olric.service"}
|
||||
infraServices := []string{"orama-ipfs.service", "orama-olric.service"}
|
||||
|
||||
// Add Anyone service if configured (relay or client)
|
||||
if ps.IsAnyoneRelay() {
|
||||
@ -754,12 +754,12 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
}
|
||||
if ps.portChecker.IsPortInUse(orPort) {
|
||||
ps.logf(" ℹ️ ORPort %d is already in use (existing anon relay running)", orPort)
|
||||
ps.logf(" ℹ️ Skipping debros-anyone-relay startup - using existing service")
|
||||
ps.logf(" ℹ️ Skipping orama-anyone-relay startup - using existing service")
|
||||
} else {
|
||||
infraServices = append(infraServices, "debros-anyone-relay.service")
|
||||
infraServices = append(infraServices, "orama-anyone-relay.service")
|
||||
}
|
||||
} else if ps.IsAnyoneClient() {
|
||||
infraServices = append(infraServices, "debros-anyone-relay.service")
|
||||
infraServices = append(infraServices, "orama-anyone-relay.service")
|
||||
}
|
||||
|
||||
for _, svc := range infraServices {
|
||||
@ -774,17 +774,17 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Start IPFS Cluster
|
||||
if err := ps.serviceController.RestartService("debros-ipfs-cluster.service"); err != nil {
|
||||
ps.logf(" ⚠️ Failed to start debros-ipfs-cluster.service: %v", err)
|
||||
if err := ps.serviceController.RestartService("orama-ipfs-cluster.service"); err != nil {
|
||||
ps.logf(" ⚠️ Failed to start orama-ipfs-cluster.service: %v", err)
|
||||
} else {
|
||||
ps.logf(" - debros-ipfs-cluster.service started")
|
||||
ps.logf(" - orama-ipfs-cluster.service started")
|
||||
}
|
||||
|
||||
// Start node service (gateway is embedded in node, no separate service needed)
|
||||
if err := ps.serviceController.RestartService("debros-node.service"); err != nil {
|
||||
ps.logf(" ⚠️ Failed to start debros-node.service: %v", err)
|
||||
if err := ps.serviceController.RestartService("orama-node.service"); err != nil {
|
||||
ps.logf(" ⚠️ Failed to start orama-node.service: %v", err)
|
||||
} else {
|
||||
ps.logf(" - debros-node.service started (with embedded gateway)")
|
||||
ps.logf(" - orama-node.service started (with embedded gateway)")
|
||||
}
|
||||
|
||||
// Start CoreDNS (nameserver nodes only)
|
||||
@ -798,7 +798,7 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
}
|
||||
}
|
||||
// Start Caddy on ALL nodes (any node may host namespaces and need TLS)
|
||||
// Caddy depends on debros-node.service (gateway on :6001), so start after node
|
||||
// Caddy depends on orama-node.service (gateway on :6001), so start after node
|
||||
if _, err := os.Stat("/usr/bin/caddy"); err == nil {
|
||||
if err := ps.serviceController.RestartService("caddy.service"); err != nil {
|
||||
ps.logf(" ⚠️ Failed to start caddy.service: %v", err)
|
||||
@ -955,8 +955,8 @@ func (ps *ProductionSetup) LogSetupComplete(peerID string) {
|
||||
ps.logf(strings.Repeat("=", 70))
|
||||
ps.logf("\nNode Peer ID: %s", peerID)
|
||||
ps.logf("\nService Management:")
|
||||
ps.logf(" systemctl status debros-ipfs")
|
||||
ps.logf(" journalctl -u debros-node -f")
|
||||
ps.logf(" systemctl status orama-ipfs")
|
||||
ps.logf(" journalctl -u orama-node -f")
|
||||
ps.logf(" tail -f %s/logs/node.log", ps.oramaDir)
|
||||
ps.logf("\nLog Files:")
|
||||
ps.logf(" %s/logs/ipfs.log", ps.oramaDir)
|
||||
@ -969,7 +969,7 @@ func (ps *ProductionSetup) LogSetupComplete(peerID string) {
|
||||
if ps.IsAnyoneRelay() {
|
||||
ps.logf(" /var/log/anon/notices.log (Anyone Relay)")
|
||||
ps.logf("\nStart All Services:")
|
||||
ps.logf(" systemctl start debros-ipfs debros-ipfs-cluster debros-olric debros-anyone-relay debros-node")
|
||||
ps.logf(" systemctl start orama-ipfs orama-ipfs-cluster orama-olric orama-anyone-relay orama-node")
|
||||
ps.logf("\nAnyone Relay Operator:")
|
||||
ps.logf(" ORPort: %d", ps.anyoneRelayConfig.ORPort)
|
||||
ps.logf(" Wallet: %s", ps.anyoneRelayConfig.Wallet)
|
||||
@ -978,7 +978,7 @@ func (ps *ProductionSetup) LogSetupComplete(peerID string) {
|
||||
ps.logf(" IMPORTANT: You need 100 $ANYONE tokens in your wallet to receive rewards")
|
||||
} else {
|
||||
ps.logf("\nStart All Services:")
|
||||
ps.logf(" systemctl start debros-ipfs debros-ipfs-cluster debros-olric debros-node")
|
||||
ps.logf(" systemctl start orama-ipfs orama-ipfs-cluster orama-olric orama-node")
|
||||
}
|
||||
|
||||
ps.logf("\nVerify Installation:")
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
type FilesystemProvisioner struct {
|
||||
oramaHome string
|
||||
oramaDir string
|
||||
logWriter interface{} // Can be io.Writer for logging
|
||||
logWriter interface{} // Can be io.Writer for logging
|
||||
}
|
||||
|
||||
// NewFilesystemProvisioner creates a new provisioner
|
||||
@ -81,30 +81,30 @@ func (fp *FilesystemProvisioner) EnsureDirectoryStructure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FixOwnership changes ownership of .orama directory to debros user
|
||||
// FixOwnership changes ownership of .orama directory to orama user
|
||||
func (fp *FilesystemProvisioner) FixOwnership() error {
|
||||
// Fix entire .orama directory recursively (includes all data, configs, logs, etc.)
|
||||
cmd := exec.Command("chown", "-R", "debros:debros", fp.oramaDir)
|
||||
cmd := exec.Command("chown", "-R", "orama:orama", fp.oramaDir)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to set ownership for %s: %w\nOutput: %s", fp.oramaDir, err, string(output))
|
||||
}
|
||||
|
||||
// Also fix home directory ownership
|
||||
cmd = exec.Command("chown", "debros:debros", fp.oramaHome)
|
||||
cmd = exec.Command("chown", "orama:orama", fp.oramaHome)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to set ownership for %s: %w\nOutput: %s", fp.oramaHome, err, string(output))
|
||||
}
|
||||
|
||||
// Fix bin directory
|
||||
binDir := filepath.Join(fp.oramaHome, "bin")
|
||||
cmd = exec.Command("chown", "-R", "debros:debros", binDir)
|
||||
cmd = exec.Command("chown", "-R", "orama:orama", binDir)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to set ownership for %s: %w\nOutput: %s", binDir, err, string(output))
|
||||
}
|
||||
|
||||
// Fix npm cache directory
|
||||
npmDir := filepath.Join(fp.oramaHome, ".npm")
|
||||
cmd = exec.Command("chown", "-R", "debros:debros", npmDir)
|
||||
cmd = exec.Command("chown", "-R", "orama:orama", npmDir)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to set ownership for %s: %w\nOutput: %s", npmDir, err, string(output))
|
||||
}
|
||||
@ -157,8 +157,8 @@ func (up *UserProvisioner) SetupSudoersAccess(invokerUser string) error {
|
||||
return nil // Skip if no invoker
|
||||
}
|
||||
|
||||
sudoersRule := fmt.Sprintf("%s ALL=(debros) NOPASSWD: ALL\n", invokerUser)
|
||||
sudoersFile := "/etc/sudoers.d/debros-access"
|
||||
sudoersRule := fmt.Sprintf("%s ALL=(orama) NOPASSWD: ALL\n", invokerUser)
|
||||
sudoersFile := "/etc/sudoers.d/orama-access"
|
||||
|
||||
// Check if rule already exists
|
||||
if existing, err := os.ReadFile(sudoersFile); err == nil {
|
||||
@ -182,31 +182,31 @@ func (up *UserProvisioner) SetupSudoersAccess(invokerUser string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupDeploymentSudoers configures the debros user with permissions needed for
|
||||
// SetupDeploymentSudoers configures the orama user with permissions needed for
|
||||
// managing user deployments via systemd services.
|
||||
func (up *UserProvisioner) SetupDeploymentSudoers() error {
|
||||
sudoersFile := "/etc/sudoers.d/debros-deployments"
|
||||
sudoersFile := "/etc/sudoers.d/orama-deployments"
|
||||
|
||||
// Check if already configured
|
||||
if _, err := os.Stat(sudoersFile); err == nil {
|
||||
return nil // Already configured
|
||||
}
|
||||
|
||||
sudoersContent := `# DeBros Network - Deployment Management Permissions
|
||||
# Allows debros user to manage systemd services for user deployments
|
||||
sudoersContent := `# Orama Network - Deployment Management Permissions
|
||||
# Allows orama user to manage systemd services for user deployments
|
||||
|
||||
# Systemd service management for orama-deploy-* services
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl start orama-deploy-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop orama-deploy-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart orama-deploy-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable orama-deploy-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable orama-deploy-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl status orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl start orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable orama-deploy-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl status orama-deploy-*
|
||||
|
||||
# Service file management (tee to write, rm to remove)
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/systemd/system/orama-deploy-*.service
|
||||
debros ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/orama-deploy-*.service
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/systemd/system/orama-deploy-*.service
|
||||
orama ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/orama-deploy-*.service
|
||||
`
|
||||
|
||||
// Write sudoers rule
|
||||
@ -224,36 +224,36 @@ debros ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/orama-deploy-*.service
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupNamespaceSudoers configures the debros user with permissions needed for
|
||||
// SetupNamespaceSudoers configures the orama user with permissions needed for
|
||||
// managing namespace cluster services via systemd.
|
||||
func (up *UserProvisioner) SetupNamespaceSudoers() error {
|
||||
sudoersFile := "/etc/sudoers.d/debros-namespaces"
|
||||
sudoersFile := "/etc/sudoers.d/orama-namespaces"
|
||||
|
||||
// Check if already configured
|
||||
if _, err := os.Stat(sudoersFile); err == nil {
|
||||
return nil // Already configured
|
||||
}
|
||||
|
||||
sudoersContent := `# DeBros Network - Namespace Cluster Management Permissions
|
||||
# Allows debros user to manage systemd services for namespace clusters
|
||||
sudoersContent := `# Orama Network - Namespace Cluster Management Permissions
|
||||
# Allows orama user to manage systemd services for namespace clusters
|
||||
|
||||
# Systemd service management for debros-namespace-* services
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl start debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl status debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active debros-namespace-*
|
||||
# Systemd service management for orama-namespace-* services
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl start orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl status orama-namespace-*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active orama-namespace-*
|
||||
|
||||
# Service file management (tee to write, rm to remove)
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/systemd/system/debros-namespace-*.service
|
||||
debros ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/debros-namespace-*.service
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/systemd/system/orama-namespace-*.service
|
||||
orama ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/orama-namespace-*.service
|
||||
|
||||
# Environment file management for namespace services
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /home/debros/.orama/namespace/*/env/*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /home/debros/.orama/namespace/*/env
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/tee /home/orama/.orama/namespace/*/env/*
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /home/orama/.orama/namespace/*/env
|
||||
`
|
||||
|
||||
// Write sudoers rule
|
||||
@ -271,17 +271,17 @@ debros ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /home/debros/.orama/namespace/*/env
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWireGuardSudoers configures the debros user with permissions to manage WireGuard
|
||||
// SetupWireGuardSudoers configures the orama user with permissions to manage WireGuard
|
||||
func (up *UserProvisioner) SetupWireGuardSudoers() error {
|
||||
sudoersFile := "/etc/sudoers.d/debros-wireguard"
|
||||
sudoersFile := "/etc/sudoers.d/orama-wireguard"
|
||||
|
||||
sudoersContent := `# DeBros Network - WireGuard Management Permissions
|
||||
# Allows debros user to manage WireGuard peers
|
||||
sudoersContent := `# Orama Network - WireGuard Management Permissions
|
||||
# Allows orama user to manage WireGuard peers
|
||||
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/wg set wg0 *
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/wg show wg0
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/wg showconf wg0
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/wireguard/wg0.conf
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/wg set wg0 *
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/wg show wg0
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/wg showconf wg0
|
||||
orama ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/wireguard/wg0.conf
|
||||
`
|
||||
|
||||
// Write sudoers rule (always overwrite to ensure latest)
|
||||
|
||||
@ -34,8 +34,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME=%[1]s
|
||||
Environment=IPFS_PATH=%[2]s
|
||||
ExecStartPre=/bin/bash -c 'if [ -f %[3]s/secrets/swarm.key ] && [ ! -f %[2]s/swarm.key ]; then cp %[3]s/secrets/swarm.key %[2]s/swarm.key && chmod 600 %[2]s/swarm.key; fi'
|
||||
@ -44,7 +44,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:%[4]s
|
||||
StandardError=append:%[4]s
|
||||
SyslogIdentifier=debros-ipfs
|
||||
SyslogIdentifier=orama-ipfs
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
@ -80,14 +80,14 @@ func (ssg *SystemdServiceGenerator) GenerateIPFSClusterService(clusterBinary str
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=IPFS Cluster Service
|
||||
After=debros-ipfs.service
|
||||
Wants=debros-ipfs.service
|
||||
Requires=debros-ipfs.service
|
||||
After=orama-ipfs.service
|
||||
Wants=orama-ipfs.service
|
||||
Requires=orama-ipfs.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory=%[1]s
|
||||
Environment=HOME=%[1]s
|
||||
Environment=IPFS_CLUSTER_PATH=%[2]s
|
||||
@ -99,7 +99,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:%[3]s
|
||||
StandardError=append:%[3]s
|
||||
SyslogIdentifier=debros-ipfs-cluster
|
||||
SyslogIdentifier=orama-ipfs-cluster
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
@ -150,15 +150,15 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME=%[1]s
|
||||
ExecStart=%[5]s %[2]s
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:%[3]s
|
||||
StandardError=append:%[3]s
|
||||
SyslogIdentifier=debros-rqlite
|
||||
SyslogIdentifier=orama-rqlite
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
@ -191,8 +191,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME=%[1]s
|
||||
Environment=OLRIC_SERVER_CONFIG=%[2]s
|
||||
ExecStart=%[5]s
|
||||
@ -222,7 +222,7 @@ WantedBy=multi-user.target
|
||||
`, ssg.oramaHome, olricConfigPath, logFile, ssg.oramaDir, olricBinary)
|
||||
}
|
||||
|
||||
// GenerateNodeService generates the DeBros Node systemd unit
|
||||
// GenerateNodeService generates the Orama Node systemd unit
|
||||
func (ssg *SystemdServiceGenerator) GenerateNodeService() string {
|
||||
configFile := "node.yaml"
|
||||
logFile := filepath.Join(ssg.oramaDir, "logs", "node.log")
|
||||
@ -230,15 +230,15 @@ func (ssg *SystemdServiceGenerator) GenerateNodeService() string {
|
||||
// Use absolute paths directly as they will be resolved by systemd at runtime
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=DeBros Network Node
|
||||
After=debros-ipfs-cluster.service debros-olric.service wg-quick@wg0.service
|
||||
Wants=debros-ipfs-cluster.service debros-olric.service
|
||||
Description=Orama Network Node
|
||||
After=orama-ipfs-cluster.service orama-olric.service wg-quick@wg0.service
|
||||
Wants=orama-ipfs-cluster.service orama-olric.service
|
||||
Requires=wg-quick@wg0.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory=%[1]s
|
||||
Environment=HOME=%[1]s
|
||||
ExecStart=%[1]s/bin/orama-node --config %[2]s/configs/%[3]s
|
||||
@ -246,7 +246,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:%[4]s
|
||||
StandardError=append:%[4]s
|
||||
SyslogIdentifier=debros-node
|
||||
SyslogIdentifier=orama-node
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
@ -269,18 +269,18 @@ WantedBy=multi-user.target
|
||||
`, ssg.oramaHome, ssg.oramaDir, configFile, logFile)
|
||||
}
|
||||
|
||||
// GenerateGatewayService generates the DeBros Gateway systemd unit
|
||||
// GenerateGatewayService generates the Orama Gateway systemd unit
|
||||
func (ssg *SystemdServiceGenerator) GenerateGatewayService() string {
|
||||
logFile := filepath.Join(ssg.oramaDir, "logs", "gateway.log")
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=DeBros Gateway
|
||||
After=debros-node.service debros-olric.service
|
||||
Wants=debros-node.service debros-olric.service
|
||||
Description=Orama Gateway
|
||||
After=orama-node.service orama-olric.service
|
||||
Wants=orama-node.service orama-olric.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory=%[1]s
|
||||
Environment=HOME=%[1]s
|
||||
ExecStart=%[1]s/bin/gateway --config %[2]s/data/gateway.yaml
|
||||
@ -288,7 +288,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=append:%[3]s
|
||||
StandardError=append:%[3]s
|
||||
SyslogIdentifier=debros-gateway
|
||||
SyslogIdentifier=orama-gateway
|
||||
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
@ -325,8 +325,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME=%[1]s
|
||||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin
|
||||
WorkingDirectory=%[1]s
|
||||
@ -400,8 +400,8 @@ func (ssg *SystemdServiceGenerator) GenerateCoreDNSService() string {
|
||||
return `[Unit]
|
||||
Description=CoreDNS DNS Server with RQLite backend
|
||||
Documentation=https://coredns.io
|
||||
After=network-online.target debros-node.service
|
||||
Wants=network-online.target debros-node.service
|
||||
After=network-online.target orama-node.service
|
||||
Wants=network-online.target orama-node.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
@ -429,9 +429,9 @@ func (ssg *SystemdServiceGenerator) GenerateCaddyService() string {
|
||||
return `[Unit]
|
||||
Description=Caddy HTTP/2 Server
|
||||
Documentation=https://caddyserver.com/docs/
|
||||
After=network-online.target debros-node.service coredns.service
|
||||
After=network-online.target orama-node.service coredns.service
|
||||
Wants=network-online.target
|
||||
Wants=debros-node.service
|
||||
Wants=orama-node.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
@ -47,8 +47,8 @@ func TestGenerateRQLiteService(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ssg := &SystemdServiceGenerator{
|
||||
oramaHome: "/home/debros",
|
||||
oramaDir: "/home/debros/.orama",
|
||||
oramaHome: "/home/orama",
|
||||
oramaDir: "/home/orama/.orama",
|
||||
}
|
||||
|
||||
unit := ssg.GenerateRQLiteService("/usr/local/bin/rqlited", 5001, 7001, tt.joinAddr, tt.advertiseIP)
|
||||
@ -81,8 +81,8 @@ func TestGenerateRQLiteService(t *testing.T) {
|
||||
// TestGenerateRQLiteServiceArgs verifies the ExecStart command arguments
|
||||
func TestGenerateRQLiteServiceArgs(t *testing.T) {
|
||||
ssg := &SystemdServiceGenerator{
|
||||
oramaHome: "/home/debros",
|
||||
oramaDir: "/home/debros/.orama",
|
||||
oramaHome: "/home/orama",
|
||||
oramaDir: "/home/orama/.orama",
|
||||
}
|
||||
|
||||
unit := ssg.GenerateRQLiteService("/usr/local/bin/rqlited", 5001, 7001, "10.0.0.1:7001", "10.0.0.2")
|
||||
|
||||
@ -145,7 +145,7 @@ func (wp *WireGuardProvisioner) WriteConfig() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to sudo tee (for non-root, e.g. debros user)
|
||||
// Fallback to sudo tee (for non-root, e.g. orama user)
|
||||
cmd := exec.Command("sudo", "tee", confPath)
|
||||
cmd.Stdin = strings.NewReader(content)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
|
||||
@ -17,8 +17,8 @@ type NodeConfigData struct {
|
||||
P2PPort int
|
||||
DataDir string
|
||||
RQLiteHTTPPort int
|
||||
RQLiteRaftPort int // External Raft port for advertisement (7001 for SNI)
|
||||
RQLiteRaftInternalPort int // Internal Raft port for local binding (7002 when SNI enabled)
|
||||
RQLiteRaftPort int // External Raft port for advertisement (7001 for SNI)
|
||||
RQLiteRaftInternalPort int // Internal Raft port for local binding (7002 when SNI enabled)
|
||||
RQLiteJoinAddress string // Optional: join address for joining existing cluster
|
||||
BootstrapPeers []string // List of peer multiaddrs to connect to
|
||||
ClusterAPIPort int
|
||||
@ -58,13 +58,13 @@ type GatewayConfigData struct {
|
||||
|
||||
// OlricConfigData holds parameters for olric.yaml rendering
|
||||
type OlricConfigData struct {
|
||||
ServerBindAddr string // HTTP API bind address (127.0.0.1 for security)
|
||||
HTTPPort int
|
||||
MemberlistBindAddr string // Memberlist bind address (WG IP for clustering)
|
||||
MemberlistPort int
|
||||
MemberlistEnvironment string // "local", "lan", or "wan"
|
||||
ServerBindAddr string // HTTP API bind address (127.0.0.1 for security)
|
||||
HTTPPort int
|
||||
MemberlistBindAddr string // Memberlist bind address (WG IP for clustering)
|
||||
MemberlistPort int
|
||||
MemberlistEnvironment string // "local", "lan", or "wan"
|
||||
MemberlistAdvertiseAddr string // Advertise address (WG IP) so other nodes can reach us
|
||||
Peers []string // Seed peers for memberlist (host:port)
|
||||
Peers []string // Seed peers for memberlist (host:port)
|
||||
}
|
||||
|
||||
// SystemdIPFSData holds parameters for systemd IPFS service rendering
|
||||
@ -72,33 +72,33 @@ type SystemdIPFSData struct {
|
||||
HomeDir string
|
||||
IPFSRepoPath string
|
||||
SecretsDir string
|
||||
OramaDir string
|
||||
OramaDir string
|
||||
}
|
||||
|
||||
// SystemdIPFSClusterData holds parameters for systemd IPFS Cluster service rendering
|
||||
type SystemdIPFSClusterData struct {
|
||||
HomeDir string
|
||||
ClusterPath string
|
||||
OramaDir string
|
||||
OramaDir string
|
||||
}
|
||||
|
||||
// SystemdOlricData holds parameters for systemd Olric service rendering
|
||||
type SystemdOlricData struct {
|
||||
HomeDir string
|
||||
ConfigPath string
|
||||
OramaDir string
|
||||
OramaDir string
|
||||
}
|
||||
|
||||
// SystemdNodeData holds parameters for systemd Node service rendering
|
||||
type SystemdNodeData struct {
|
||||
HomeDir string
|
||||
ConfigFile string
|
||||
OramaDir string
|
||||
OramaDir string
|
||||
}
|
||||
|
||||
// SystemdGatewayData holds parameters for systemd Gateway service rendering
|
||||
type SystemdGatewayData struct {
|
||||
HomeDir string
|
||||
HomeDir string
|
||||
OramaDir string
|
||||
}
|
||||
|
||||
@ -132,12 +132,12 @@ func RenderOlricService(data SystemdOlricData) (string, error) {
|
||||
return renderTemplate("systemd_olric.service", data)
|
||||
}
|
||||
|
||||
// RenderNodeService renders the DeBros Node systemd service template
|
||||
// RenderNodeService renders the Orama Node systemd service template
|
||||
func RenderNodeService(data SystemdNodeData) (string, error) {
|
||||
return renderTemplate("systemd_node.service", data)
|
||||
}
|
||||
|
||||
// RenderGatewayService renders the DeBros Gateway systemd service template
|
||||
// RenderGatewayService renders the Orama Gateway systemd service template
|
||||
func RenderGatewayService(data SystemdGatewayData) (string, error) {
|
||||
return renderTemplate("systemd_gateway.service", data)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ func TestRenderNodeConfig(t *testing.T) {
|
||||
data := NodeConfigData{
|
||||
NodeID: "node2",
|
||||
P2PPort: 4002,
|
||||
DataDir: "/home/debros/.orama/node2",
|
||||
DataDir: "/home/orama/.orama/node2",
|
||||
RQLiteHTTPPort: 5002,
|
||||
RQLiteRaftPort: 7002,
|
||||
RQLiteJoinAddress: "localhost:5001",
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
[Unit]
|
||||
Description=DeBros Gateway
|
||||
After=debros-node.service
|
||||
Wants=debros-node.service
|
||||
Description=Orama Gateway
|
||||
After=orama-node.service
|
||||
Wants=orama-node.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory={{.HomeDir}}
|
||||
Environment=HOME={{.HomeDir}}
|
||||
ExecStart={{.HomeDir}}/bin/gateway --config {{.OramaDir}}/data/gateway.yaml
|
||||
@ -14,7 +14,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=debros-gateway
|
||||
SyslogIdentifier=orama-gateway
|
||||
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
|
||||
@ -5,8 +5,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME={{.HomeDir}}
|
||||
Environment=IPFS_PATH={{.IPFSRepoPath}}
|
||||
ExecStartPre=/bin/bash -c 'if [ -f {{.SecretsDir}}/swarm.key ] && [ ! -f {{.IPFSRepoPath}}/swarm.key ]; then cp {{.SecretsDir}}/swarm.key {{.IPFSRepoPath}}/swarm.key && chmod 600 {{.IPFSRepoPath}}/swarm.key; fi'
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
[Unit]
|
||||
Description=IPFS Cluster Service ({{.NodeType}})
|
||||
After=debros-ipfs-{{.NodeType}}.service
|
||||
Wants=debros-ipfs-{{.NodeType}}.service
|
||||
Requires=debros-ipfs-{{.NodeType}}.service
|
||||
After=orama-ipfs-{{.NodeType}}.service
|
||||
Wants=orama-ipfs-{{.NodeType}}.service
|
||||
Requires=orama-ipfs-{{.NodeType}}.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory={{.HomeDir}}
|
||||
Environment=HOME={{.HomeDir}}
|
||||
Environment=CLUSTER_PATH={{.ClusterPath}}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
[Unit]
|
||||
Description=DeBros Network Node ({{.NodeType}})
|
||||
After=debros-ipfs-cluster-{{.NodeType}}.service
|
||||
Wants=debros-ipfs-cluster-{{.NodeType}}.service
|
||||
Requires=debros-ipfs-cluster-{{.NodeType}}.service
|
||||
Description=Orama Network Node ({{.NodeType}})
|
||||
After=orama-ipfs-cluster-{{.NodeType}}.service
|
||||
Wants=orama-ipfs-cluster-{{.NodeType}}.service
|
||||
Requires=orama-ipfs-cluster-{{.NodeType}}.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
WorkingDirectory={{.HomeDir}}
|
||||
Environment=HOME={{.HomeDir}}
|
||||
ExecStart={{.HomeDir}}/bin/orama-node --config {{.OramaDir}}/configs/{{.ConfigFile}}
|
||||
@ -15,7 +15,7 @@ Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=debros-node-{{.NodeType}}
|
||||
SyslogIdentifier=orama-node-{{.NodeType}}
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
|
||||
@ -5,8 +5,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=debros
|
||||
Group=debros
|
||||
User=orama
|
||||
Group=orama
|
||||
Environment=HOME={{.HomeDir}}
|
||||
Environment=OLRIC_SERVER_CONFIG={{.ConfigPath}}
|
||||
ExecStart=/usr/local/bin/olric-server
|
||||
|
||||
@ -127,7 +127,7 @@ func (g *Gateway) anonProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Set default User-Agent if not provided
|
||||
if proxyReq.Header.Get("User-Agent") == "" {
|
||||
proxyReq.Header.Set("User-Agent", "DeBros-Gateway/1.0")
|
||||
proxyReq.Header.Set("User-Agent", "Orama-Gateway/1.0")
|
||||
}
|
||||
|
||||
// Log the proxy request
|
||||
|
||||
@ -157,7 +157,7 @@ func (s *Service) ParseAndVerifyJWT(token string) (*JWTClaims, error) {
|
||||
return nil, errors.New("invalid claims json")
|
||||
}
|
||||
// Validate issuer
|
||||
if claims.Iss != "debros-gateway" {
|
||||
if claims.Iss != "orama-gateway" {
|
||||
return nil, errors.New("invalid issuer")
|
||||
}
|
||||
// Validate registered claims
|
||||
@ -199,7 +199,7 @@ func (s *Service) generateEdDSAJWT(ns, subject string, ttl time.Duration) (strin
|
||||
now := time.Now().UTC()
|
||||
exp := now.Add(ttl)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway",
|
||||
"iss": "orama-gateway",
|
||||
"sub": subject,
|
||||
"aud": "gateway",
|
||||
"iat": now.Unix(),
|
||||
@ -229,7 +229,7 @@ func (s *Service) generateRSAJWT(ns, subject string, ttl time.Duration) (string,
|
||||
now := time.Now().UTC()
|
||||
exp := now.Add(ttl)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway",
|
||||
"iss": "orama-gateway",
|
||||
"sub": subject,
|
||||
"aud": "gateway",
|
||||
"iat": now.Unix(),
|
||||
|
||||
@ -138,8 +138,8 @@ func TestJWTFlow(t *testing.T) {
|
||||
t.Errorf("expected namespace %s, got %s", ns, claims.Namespace)
|
||||
}
|
||||
|
||||
if claims.Iss != "debros-gateway" {
|
||||
t.Errorf("expected issuer debros-gateway, got %s", claims.Iss)
|
||||
if claims.Iss != "orama-gateway" {
|
||||
t.Errorf("expected issuer orama-gateway, got %s", claims.Iss)
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ func TestAlgorithmConfusion_Rejected(t *testing.T) {
|
||||
header := map[string]string{"alg": "none", "typ": "JWT"}
|
||||
hb, _ := json.Marshal(header)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iss": "orama-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iat": time.Now().Unix(), "nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(), "namespace": "test-ns",
|
||||
}
|
||||
@ -275,7 +275,7 @@ func TestAlgorithmConfusion_Rejected(t *testing.T) {
|
||||
header := map[string]string{"alg": "HS256", "typ": "JWT", "kid": s.keyID}
|
||||
hb, _ := json.Marshal(header)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iss": "orama-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iat": time.Now().Unix(), "nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(), "namespace": "test-ns",
|
||||
}
|
||||
@ -295,7 +295,7 @@ func TestAlgorithmConfusion_Rejected(t *testing.T) {
|
||||
header := map[string]string{"alg": "RS256", "typ": "JWT", "kid": s.edKeyID}
|
||||
hb, _ := json.Marshal(header)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iss": "orama-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iat": time.Now().Unix(), "nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(), "namespace": "test-ns",
|
||||
}
|
||||
@ -321,7 +321,7 @@ func TestAlgorithmConfusion_Rejected(t *testing.T) {
|
||||
header := map[string]string{"alg": "RS256", "typ": "JWT", "kid": "unknown-kid-123"}
|
||||
hb, _ := json.Marshal(header)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iss": "orama-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iat": time.Now().Unix(), "nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(), "namespace": "test-ns",
|
||||
}
|
||||
@ -341,7 +341,7 @@ func TestAlgorithmConfusion_Rejected(t *testing.T) {
|
||||
header := map[string]string{"alg": "EdDSA", "typ": "JWT"}
|
||||
hb, _ := json.Marshal(header)
|
||||
payload := map[string]any{
|
||||
"iss": "debros-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iss": "orama-gateway", "sub": "attacker", "aud": "gateway",
|
||||
"iat": time.Now().Unix(), "nbf": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour).Unix(), "namespace": "test-ns",
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ type PeerInfo struct {
|
||||
type Handler struct {
|
||||
logger *zap.Logger
|
||||
rqliteClient rqlite.Client
|
||||
oramaDir string // e.g., /home/debros/.orama
|
||||
oramaDir string // e.g., /home/orama/.orama
|
||||
}
|
||||
|
||||
// NewHandler creates a new join handler
|
||||
|
||||
@ -59,7 +59,7 @@ func NewHTTPSGateway(logger *logging.ColoredLogger, cfg *config.HTTPGatewayConfi
|
||||
// Use Let's Encrypt STAGING (consistent with SNI gateway)
|
||||
cacheDir := cfg.HTTPS.CacheDir
|
||||
if cacheDir == "" {
|
||||
cacheDir = "/home/debros/.orama/tls-cache"
|
||||
cacheDir = "/home/orama/.orama/tls-cache"
|
||||
}
|
||||
|
||||
// Use Let's Encrypt STAGING - provides higher rate limits for testing/development
|
||||
|
||||
@ -32,7 +32,7 @@ func TestJWTGenerateAndParse(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("verify err: %v", err)
|
||||
}
|
||||
if claims.Namespace != "ns1" || claims.Sub != "subj" || claims.Aud != "gateway" || claims.Iss != "debros-gateway" {
|
||||
if claims.Namespace != "ns1" || claims.Sub != "subj" || claims.Aud != "gateway" || claims.Iss != "orama-gateway" {
|
||||
t.Fatalf("unexpected claims: %+v", claims)
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func TestExtractAPIKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDomainRoutingMiddleware_NonDebrosNetwork tests that non-debros domains pass through
|
||||
// TestDomainRoutingMiddleware_NonDebrosNetwork tests that non-orama domains pass through
|
||||
func TestDomainRoutingMiddleware_NonDebrosNetwork(t *testing.T) {
|
||||
nextCalled := false
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@ -46,7 +46,7 @@ func TestDomainRoutingMiddleware_NonDebrosNetwork(t *testing.T) {
|
||||
middleware.ServeHTTP(rr, req)
|
||||
|
||||
if !nextCalled {
|
||||
t.Error("Expected next handler to be called for non-debros domain")
|
||||
t.Error("Expected next handler to be called for non-orama domain")
|
||||
}
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
@ -379,10 +379,10 @@ func TestSecurityHeadersMiddleware(t *testing.T) {
|
||||
|
||||
expected := map[string]string{
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "DENY",
|
||||
"X-Xss-Protection": "0",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
|
||||
"X-Frame-Options": "DENY",
|
||||
"X-Xss-Protection": "0",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
|
||||
}
|
||||
for header, want := range expected {
|
||||
got := rr.Header().Get(header)
|
||||
|
||||
@ -42,7 +42,7 @@ func checkAnyonePerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
|
||||
if a.RelayActive {
|
||||
r = append(r, inspector.Pass("anyone.relay_active", "Anyone relay service active", anyoneSub, node,
|
||||
"debros-anyone-relay is active", inspector.High))
|
||||
"orama-anyone-relay is active", inspector.High))
|
||||
}
|
||||
|
||||
// --- Client-mode checks ---
|
||||
@ -133,7 +133,7 @@ func checkAnyonePerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
// --- Legacy client checks (if also running client service) ---
|
||||
if a.ClientActive {
|
||||
r = append(r, inspector.Pass("anyone.client_active", "Anyone client service active", anyoneSub, node,
|
||||
"debros-anyone-client is active", inspector.High))
|
||||
"orama-anyone-client is active", inspector.High))
|
||||
|
||||
if a.SocksListening {
|
||||
r = append(r, inspector.Pass("anyone.socks_listening", "SOCKS5 port 9050 listening", anyoneSub, node,
|
||||
|
||||
@ -55,7 +55,7 @@ func TestCheckAnyone_HealthyRelay(t *testing.T) {
|
||||
func TestCheckAnyone_HealthyClient(t *testing.T) {
|
||||
nd := makeNodeData("1.1.1.1", "nameserver")
|
||||
nd.Anyone = &inspector.AnyoneData{
|
||||
RelayActive: true, // service is debros-anyone-relay for both modes
|
||||
RelayActive: true, // service is orama-anyone-relay for both modes
|
||||
Mode: "client",
|
||||
SocksListening: true,
|
||||
ControlListening: true,
|
||||
|
||||
@ -36,20 +36,20 @@ func checkIPFSPerNode(nd *inspector.NodeData, data *inspector.ClusterData) []ins
|
||||
// 3.1 IPFS daemon running
|
||||
if ipfs.DaemonActive {
|
||||
r = append(r, inspector.Pass("ipfs.daemon_active", "IPFS daemon active", ipfsSub, node,
|
||||
"debros-ipfs is active", inspector.Critical))
|
||||
"orama-ipfs is active", inspector.Critical))
|
||||
} else {
|
||||
r = append(r, inspector.Fail("ipfs.daemon_active", "IPFS daemon active", ipfsSub, node,
|
||||
"debros-ipfs is not active", inspector.Critical))
|
||||
"orama-ipfs is not active", inspector.Critical))
|
||||
return r
|
||||
}
|
||||
|
||||
// 3.2 IPFS Cluster running
|
||||
if ipfs.ClusterActive {
|
||||
r = append(r, inspector.Pass("ipfs.cluster_active", "IPFS Cluster active", ipfsSub, node,
|
||||
"debros-ipfs-cluster is active", inspector.Critical))
|
||||
"orama-ipfs-cluster is active", inspector.Critical))
|
||||
} else {
|
||||
r = append(r, inspector.Fail("ipfs.cluster_active", "IPFS Cluster active", ipfsSub, node,
|
||||
"debros-ipfs-cluster is not active", inspector.Critical))
|
||||
"orama-ipfs-cluster is not active", inspector.Critical))
|
||||
}
|
||||
|
||||
// 3.6 Swarm peer count
|
||||
|
||||
@ -36,10 +36,10 @@ func checkOlricPerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
// 2.1 Service active
|
||||
if ol.ServiceActive {
|
||||
r = append(r, inspector.Pass("olric.service_active", "Olric service active", olricSub, node,
|
||||
"debros-olric is active", inspector.Critical))
|
||||
"orama-olric is active", inspector.Critical))
|
||||
} else {
|
||||
r = append(r, inspector.Fail("olric.service_active", "Olric service active", olricSub, node,
|
||||
"debros-olric is not active", inspector.Critical))
|
||||
"orama-olric is not active", inspector.Critical))
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ func checkSystemPerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
node := nd.Node.Name()
|
||||
|
||||
// 6.1 Core services active
|
||||
coreServices := []string{"debros-node", "debros-olric", "debros-ipfs", "debros-ipfs-cluster"}
|
||||
coreServices := []string{"orama-node", "orama-olric", "orama-ipfs", "orama-ipfs-cluster"}
|
||||
for _, svc := range coreServices {
|
||||
status, ok := sys.Services[svc]
|
||||
if !ok {
|
||||
@ -51,7 +51,7 @@ func checkSystemPerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
}
|
||||
|
||||
// 6.2 Anyone relay/client services (only check if installed, don't fail if absent)
|
||||
for _, svc := range []string{"debros-anyone-relay", "debros-anyone-client"} {
|
||||
for _, svc := range []string{"orama-anyone-relay", "orama-anyone-client"} {
|
||||
status, ok := sys.Services[svc]
|
||||
if !ok || status == "inactive" {
|
||||
continue // not installed or intentionally stopped
|
||||
@ -94,21 +94,21 @@ func checkSystemPerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
}
|
||||
}
|
||||
|
||||
// 6.6 Failed systemd units (only debros-related units count as failures)
|
||||
var debrosUnits, externalUnits []string
|
||||
// 6.6 Failed systemd units (only orama-related units count as failures)
|
||||
var oramaUnits, externalUnits []string
|
||||
for _, u := range sys.FailedUnits {
|
||||
if strings.HasPrefix(u, "debros-") || u == "wg-quick@wg0.service" || u == "caddy.service" || u == "coredns.service" {
|
||||
debrosUnits = append(debrosUnits, u)
|
||||
if strings.HasPrefix(u, "orama-") || u == "wg-quick@wg0.service" || u == "caddy.service" || u == "coredns.service" {
|
||||
oramaUnits = append(oramaUnits, u)
|
||||
} else {
|
||||
externalUnits = append(externalUnits, u)
|
||||
}
|
||||
}
|
||||
if len(debrosUnits) > 0 {
|
||||
r = append(r, inspector.Fail("system.no_failed_units", "No failed debros systemd units", systemSub, node,
|
||||
fmt.Sprintf("failed: %s", strings.Join(debrosUnits, ", ")), inspector.High))
|
||||
if len(oramaUnits) > 0 {
|
||||
r = append(r, inspector.Fail("system.no_failed_units", "No failed orama systemd units", systemSub, node,
|
||||
fmt.Sprintf("failed: %s", strings.Join(oramaUnits, ", ")), inspector.High))
|
||||
} else {
|
||||
r = append(r, inspector.Pass("system.no_failed_units", "No failed debros systemd units", systemSub, node,
|
||||
"no failed debros units", inspector.High))
|
||||
r = append(r, inspector.Pass("system.no_failed_units", "No failed orama systemd units", systemSub, node,
|
||||
"no failed orama units", inspector.High))
|
||||
}
|
||||
if len(externalUnits) > 0 {
|
||||
r = append(r, inspector.Warn("system.external_failed_units", "External systemd units healthy", systemSub, node,
|
||||
@ -217,15 +217,15 @@ func checkSystemPerNode(nd *inspector.NodeData) []inspector.CheckResult {
|
||||
|
||||
// 6.23 Process user
|
||||
if sys.ProcessUser != "" && sys.ProcessUser != "unknown" {
|
||||
if sys.ProcessUser == "debros" {
|
||||
r = append(r, inspector.Pass("system.process_user", "debros-node runs as correct user", systemSub, node,
|
||||
"user=debros", inspector.High))
|
||||
if sys.ProcessUser == "orama" {
|
||||
r = append(r, inspector.Pass("system.process_user", "orama-node runs as correct user", systemSub, node,
|
||||
"user=orama", inspector.High))
|
||||
} else if sys.ProcessUser == "root" {
|
||||
r = append(r, inspector.Warn("system.process_user", "debros-node runs as correct user", systemSub, node,
|
||||
"user=root (should be debros)", inspector.High))
|
||||
r = append(r, inspector.Warn("system.process_user", "orama-node runs as correct user", systemSub, node,
|
||||
"user=root (should be orama)", inspector.High))
|
||||
} else {
|
||||
r = append(r, inspector.Warn("system.process_user", "debros-node runs as correct user", systemSub, node,
|
||||
fmt.Sprintf("user=%s (expected debros)", sys.ProcessUser), inspector.Medium))
|
||||
r = append(r, inspector.Warn("system.process_user", "orama-node runs as correct user", systemSub, node,
|
||||
fmt.Sprintf("user=%s (expected orama)", sys.ProcessUser), inspector.Medium))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,11 +10,11 @@ func TestCheckSystem_HealthyNode(t *testing.T) {
|
||||
nd := makeNodeData("1.1.1.1", "node")
|
||||
nd.System = &inspector.SystemData{
|
||||
Services: map[string]string{
|
||||
"debros-node": "active",
|
||||
"debros-olric": "active",
|
||||
"debros-ipfs": "active",
|
||||
"debros-ipfs-cluster": "active",
|
||||
"wg-quick@wg0": "active",
|
||||
"orama-node": "active",
|
||||
"orama-olric": "active",
|
||||
"orama-ipfs": "active",
|
||||
"orama-ipfs-cluster": "active",
|
||||
"wg-quick@wg0": "active",
|
||||
},
|
||||
FailedUnits: nil,
|
||||
MemTotalMB: 8192,
|
||||
@ -31,17 +31,17 @@ func TestCheckSystem_HealthyNode(t *testing.T) {
|
||||
InodePct: 10,
|
||||
ListeningPorts: []int{5001, 3322, 6001, 4501},
|
||||
UFWActive: true,
|
||||
ProcessUser: "debros",
|
||||
ProcessUser: "orama",
|
||||
PanicCount: 0,
|
||||
}
|
||||
|
||||
data := makeCluster(map[string]*inspector.NodeData{"1.1.1.1": nd})
|
||||
results := CheckSystem(data)
|
||||
|
||||
expectStatus(t, results, "system.svc_debros_node", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_debros_olric", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_debros_ipfs", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_debros_ipfs_cluster", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_orama_node", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_orama_olric", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_orama_ipfs", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_orama_ipfs_cluster", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_wg", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.no_failed_units", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.memory", inspector.StatusPass)
|
||||
@ -63,30 +63,30 @@ func TestCheckSystem_ServiceInactive(t *testing.T) {
|
||||
nd := makeNodeData("1.1.1.1", "node")
|
||||
nd.System = &inspector.SystemData{
|
||||
Services: map[string]string{
|
||||
"debros-node": "active",
|
||||
"debros-olric": "inactive",
|
||||
"debros-ipfs": "active",
|
||||
"debros-ipfs-cluster": "failed",
|
||||
"orama-node": "active",
|
||||
"orama-olric": "inactive",
|
||||
"orama-ipfs": "active",
|
||||
"orama-ipfs-cluster": "failed",
|
||||
},
|
||||
}
|
||||
data := makeCluster(map[string]*inspector.NodeData{"1.1.1.1": nd})
|
||||
results := CheckSystem(data)
|
||||
|
||||
expectStatus(t, results, "system.svc_debros_node", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_debros_olric", inspector.StatusFail)
|
||||
expectStatus(t, results, "system.svc_debros_ipfs_cluster", inspector.StatusFail)
|
||||
expectStatus(t, results, "system.svc_orama_node", inspector.StatusPass)
|
||||
expectStatus(t, results, "system.svc_orama_olric", inspector.StatusFail)
|
||||
expectStatus(t, results, "system.svc_orama_ipfs_cluster", inspector.StatusFail)
|
||||
}
|
||||
|
||||
func TestCheckSystem_NameserverServices(t *testing.T) {
|
||||
nd := makeNodeData("5.5.5.5", "nameserver-ns1")
|
||||
nd.System = &inspector.SystemData{
|
||||
Services: map[string]string{
|
||||
"debros-node": "active",
|
||||
"debros-olric": "active",
|
||||
"debros-ipfs": "active",
|
||||
"debros-ipfs-cluster": "active",
|
||||
"coredns": "active",
|
||||
"caddy": "active",
|
||||
"orama-node": "active",
|
||||
"orama-olric": "active",
|
||||
"orama-ipfs": "active",
|
||||
"orama-ipfs-cluster": "active",
|
||||
"coredns": "active",
|
||||
"caddy": "active",
|
||||
},
|
||||
}
|
||||
data := makeCluster(map[string]*inspector.NodeData{"5.5.5.5": nd})
|
||||
@ -99,10 +99,10 @@ func TestCheckSystem_NameserverServicesNotCheckedOnRegularNode(t *testing.T) {
|
||||
nd := makeNodeData("1.1.1.1", "node")
|
||||
nd.System = &inspector.SystemData{
|
||||
Services: map[string]string{
|
||||
"debros-node": "active",
|
||||
"debros-olric": "active",
|
||||
"debros-ipfs": "active",
|
||||
"debros-ipfs-cluster": "active",
|
||||
"orama-node": "active",
|
||||
"orama-olric": "active",
|
||||
"orama-ipfs": "active",
|
||||
"orama-ipfs-cluster": "active",
|
||||
},
|
||||
}
|
||||
data := makeCluster(map[string]*inspector.NodeData{"1.1.1.1": nd})
|
||||
@ -116,7 +116,7 @@ func TestCheckSystem_FailedUnits_Debros(t *testing.T) {
|
||||
nd := makeNodeData("1.1.1.1", "node")
|
||||
nd.System = &inspector.SystemData{
|
||||
Services: map[string]string{},
|
||||
FailedUnits: []string{"debros-node.service"},
|
||||
FailedUnits: []string{"orama-node.service"},
|
||||
}
|
||||
data := makeCluster(map[string]*inspector.NodeData{"1.1.1.1": nd})
|
||||
results := CheckSystem(data)
|
||||
@ -142,9 +142,9 @@ func TestCheckSystem_Memory(t *testing.T) {
|
||||
total int
|
||||
status inspector.Status
|
||||
}{
|
||||
{"healthy", 4000, 8000, inspector.StatusPass}, // 50%
|
||||
{"elevated", 7000, 8000, inspector.StatusWarn}, // 87.5%
|
||||
{"critical", 7500, 8000, inspector.StatusFail}, // 93.75%
|
||||
{"healthy", 4000, 8000, inspector.StatusPass}, // 50%
|
||||
{"elevated", 7000, 8000, inspector.StatusWarn}, // 87.5%
|
||||
{"critical", 7500, 8000, inspector.StatusFail}, // 93.75%
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -248,7 +248,7 @@ func TestCheckSystem_ProcessUser(t *testing.T) {
|
||||
user string
|
||||
status inspector.Status
|
||||
}{
|
||||
{"correct", "debros", inspector.StatusPass},
|
||||
{"correct", "orama", inspector.StatusPass},
|
||||
{"root", "root", inspector.StatusWarn},
|
||||
{"other", "ubuntu", inspector.StatusWarn},
|
||||
}
|
||||
|
||||
@ -46,53 +46,53 @@ type NamespaceData struct {
|
||||
// RQLiteData holds parsed RQLite status from a single node.
|
||||
type RQLiteData struct {
|
||||
Responsive bool
|
||||
StatusRaw string // raw JSON from /status
|
||||
NodesRaw string // raw JSON from /nodes?nonvoters
|
||||
ReadyzRaw string // raw response from /readyz
|
||||
DebugRaw string // raw JSON from /debug/vars
|
||||
Status *RQLiteStatus // parsed /status
|
||||
StatusRaw string // raw JSON from /status
|
||||
NodesRaw string // raw JSON from /nodes?nonvoters
|
||||
ReadyzRaw string // raw response from /readyz
|
||||
DebugRaw string // raw JSON from /debug/vars
|
||||
Status *RQLiteStatus // parsed /status
|
||||
Nodes map[string]*RQLiteNode // parsed /nodes
|
||||
Readyz *RQLiteReadyz // parsed /readyz
|
||||
DebugVars *RQLiteDebugVars // parsed /debug/vars
|
||||
StrongRead bool // SELECT 1 with level=strong succeeded
|
||||
Readyz *RQLiteReadyz // parsed /readyz
|
||||
DebugVars *RQLiteDebugVars // parsed /debug/vars
|
||||
StrongRead bool // SELECT 1 with level=strong succeeded
|
||||
}
|
||||
|
||||
// RQLiteDebugVars holds metrics from /debug/vars.
|
||||
type RQLiteDebugVars struct {
|
||||
QueryErrors uint64
|
||||
ExecuteErrors uint64
|
||||
RemoteExecErrors uint64
|
||||
LeaderNotFound uint64
|
||||
SnapshotErrors uint64
|
||||
ClientRetries uint64
|
||||
ClientTimeouts uint64
|
||||
QueryErrors uint64
|
||||
ExecuteErrors uint64
|
||||
RemoteExecErrors uint64
|
||||
LeaderNotFound uint64
|
||||
SnapshotErrors uint64
|
||||
ClientRetries uint64
|
||||
ClientTimeouts uint64
|
||||
}
|
||||
|
||||
// RQLiteStatus holds parsed fields from /status.
|
||||
type RQLiteStatus struct {
|
||||
RaftState string // Leader, Follower, Candidate, Shutdown
|
||||
LeaderNodeID string // store.leader.node_id
|
||||
LeaderAddr string // store.leader.addr
|
||||
NodeID string // store.node_id
|
||||
Term uint64 // store.raft.term (current_term)
|
||||
AppliedIndex uint64 // store.raft.applied_index
|
||||
CommitIndex uint64 // store.raft.commit_index
|
||||
FsmPending uint64 // store.raft.fsm_pending
|
||||
LastContact string // store.raft.last_contact (followers only)
|
||||
LastLogIndex uint64 // store.raft.last_log_index
|
||||
LastLogTerm uint64 // store.raft.last_log_term
|
||||
NumPeers int // store.raft.num_peers (string in JSON)
|
||||
LastSnapshot uint64 // store.raft.last_snapshot_index
|
||||
Voter bool // store.raft.voter
|
||||
DBSize int64 // store.sqlite3.db_size
|
||||
DBSizeFriendly string // store.sqlite3.db_size_friendly
|
||||
DBAppliedIndex uint64 // store.db_applied_index
|
||||
FsmIndex uint64 // store.fsm_index
|
||||
Uptime string // http.uptime
|
||||
Version string // build.version
|
||||
GoVersion string // runtime.GOARCH + runtime.version
|
||||
Goroutines int // runtime.num_goroutine
|
||||
HeapAlloc uint64 // runtime.memory.heap_alloc (bytes)
|
||||
RaftState string // Leader, Follower, Candidate, Shutdown
|
||||
LeaderNodeID string // store.leader.node_id
|
||||
LeaderAddr string // store.leader.addr
|
||||
NodeID string // store.node_id
|
||||
Term uint64 // store.raft.term (current_term)
|
||||
AppliedIndex uint64 // store.raft.applied_index
|
||||
CommitIndex uint64 // store.raft.commit_index
|
||||
FsmPending uint64 // store.raft.fsm_pending
|
||||
LastContact string // store.raft.last_contact (followers only)
|
||||
LastLogIndex uint64 // store.raft.last_log_index
|
||||
LastLogTerm uint64 // store.raft.last_log_term
|
||||
NumPeers int // store.raft.num_peers (string in JSON)
|
||||
LastSnapshot uint64 // store.raft.last_snapshot_index
|
||||
Voter bool // store.raft.voter
|
||||
DBSize int64 // store.sqlite3.db_size
|
||||
DBSizeFriendly string // store.sqlite3.db_size_friendly
|
||||
DBAppliedIndex uint64 // store.db_applied_index
|
||||
FsmIndex uint64 // store.fsm_index
|
||||
Uptime string // http.uptime
|
||||
Version string // build.version
|
||||
GoVersion string // runtime.GOARCH + runtime.version
|
||||
Goroutines int // runtime.num_goroutine
|
||||
HeapAlloc uint64 // runtime.memory.heap_alloc (bytes)
|
||||
}
|
||||
|
||||
// RQLiteNode holds parsed fields from /nodes response per node.
|
||||
@ -107,11 +107,11 @@ type RQLiteNode struct {
|
||||
|
||||
// RQLiteReadyz holds parsed readiness state.
|
||||
type RQLiteReadyz struct {
|
||||
Ready bool
|
||||
Store string // "ready" or error
|
||||
Leader string // "ready" or error
|
||||
Node string // "ready" or error
|
||||
RawBody string
|
||||
Ready bool
|
||||
Store string // "ready" or error
|
||||
Leader string // "ready" or error
|
||||
Node string // "ready" or error
|
||||
RawBody string
|
||||
}
|
||||
|
||||
// OlricData holds parsed Olric status from a single node.
|
||||
@ -130,17 +130,17 @@ type OlricData struct {
|
||||
|
||||
// IPFSData holds parsed IPFS status from a single node.
|
||||
type IPFSData struct {
|
||||
DaemonActive bool
|
||||
ClusterActive bool
|
||||
SwarmPeerCount int
|
||||
DaemonActive bool
|
||||
ClusterActive bool
|
||||
SwarmPeerCount int
|
||||
ClusterPeerCount int
|
||||
RepoSizeBytes int64
|
||||
RepoMaxBytes int64
|
||||
KuboVersion string
|
||||
ClusterVersion string
|
||||
ClusterErrors int // peers reporting errors
|
||||
HasSwarmKey bool
|
||||
BootstrapEmpty bool // true if bootstrap list is empty (private swarm)
|
||||
RepoSizeBytes int64
|
||||
RepoMaxBytes int64
|
||||
KuboVersion string
|
||||
ClusterVersion string
|
||||
ClusterErrors int // peers reporting errors
|
||||
HasSwarmKey bool
|
||||
BootstrapEmpty bool // true if bootstrap list is empty (private swarm)
|
||||
}
|
||||
|
||||
// DNSData holds parsed DNS/CoreDNS status from a nameserver node.
|
||||
@ -154,29 +154,29 @@ type DNSData struct {
|
||||
CoreDNSRestarts int
|
||||
LogErrors int // error count in recent CoreDNS logs
|
||||
// Resolution tests (dig results)
|
||||
SOAResolves bool
|
||||
NSResolves bool
|
||||
NSRecordCount int
|
||||
SOAResolves bool
|
||||
NSResolves bool
|
||||
NSRecordCount int
|
||||
WildcardResolves bool
|
||||
BaseAResolves bool
|
||||
BaseAResolves bool
|
||||
// TLS
|
||||
BaseTLSDaysLeft int // -1 = failed to check
|
||||
WildTLSDaysLeft int // -1 = failed to check
|
||||
BaseTLSDaysLeft int // -1 = failed to check
|
||||
WildTLSDaysLeft int // -1 = failed to check
|
||||
// Corefile
|
||||
CorefileExists bool
|
||||
CorefileExists bool
|
||||
}
|
||||
|
||||
// WireGuardData holds parsed WireGuard status from a node.
|
||||
type WireGuardData struct {
|
||||
InterfaceUp bool
|
||||
InterfaceUp bool
|
||||
ServiceActive bool
|
||||
WgIP string
|
||||
PeerCount int
|
||||
Peers []WGPeer
|
||||
MTU int
|
||||
ListenPort int
|
||||
ConfigExists bool
|
||||
ConfigPerms string // e.g. "600"
|
||||
WgIP string
|
||||
PeerCount int
|
||||
Peers []WGPeer
|
||||
MTU int
|
||||
ListenPort int
|
||||
ConfigExists bool
|
||||
ConfigPerms string // e.g. "600"
|
||||
}
|
||||
|
||||
// WGPeer holds parsed data for a single WireGuard peer.
|
||||
@ -192,26 +192,26 @@ type WGPeer struct {
|
||||
|
||||
// SystemData holds parsed system-level data from a node.
|
||||
type SystemData struct {
|
||||
Services map[string]string // service name → status
|
||||
FailedUnits []string // systemd units in failed state
|
||||
MemTotalMB int
|
||||
MemUsedMB int
|
||||
MemFreeMB int
|
||||
DiskTotalGB string
|
||||
DiskUsedGB string
|
||||
DiskAvailGB string
|
||||
DiskUsePct int
|
||||
UptimeRaw string
|
||||
LoadAvg string
|
||||
CPUCount int
|
||||
OOMKills int
|
||||
SwapUsedMB int
|
||||
SwapTotalMB int
|
||||
InodePct int // inode usage percentage
|
||||
ListeningPorts []int // ports from ss -tlnp
|
||||
UFWActive bool
|
||||
ProcessUser string // user running debros-node (e.g. "debros")
|
||||
PanicCount int // panic/fatal in recent logs
|
||||
Services map[string]string // service name → status
|
||||
FailedUnits []string // systemd units in failed state
|
||||
MemTotalMB int
|
||||
MemUsedMB int
|
||||
MemFreeMB int
|
||||
DiskTotalGB string
|
||||
DiskUsedGB string
|
||||
DiskAvailGB string
|
||||
DiskUsePct int
|
||||
UptimeRaw string
|
||||
LoadAvg string
|
||||
CPUCount int
|
||||
OOMKills int
|
||||
SwapUsedMB int
|
||||
SwapTotalMB int
|
||||
InodePct int // inode usage percentage
|
||||
ListeningPorts []int // ports from ss -tlnp
|
||||
UFWActive bool
|
||||
ProcessUser string // user running orama-node (e.g. "orama")
|
||||
PanicCount int // panic/fatal in recent logs
|
||||
}
|
||||
|
||||
// NetworkData holds parsed network-level data from a node.
|
||||
@ -227,17 +227,17 @@ type NetworkData struct {
|
||||
|
||||
// AnyoneData holds parsed Anyone relay/client status from a node.
|
||||
type AnyoneData struct {
|
||||
RelayActive bool // debros-anyone-relay systemd service active
|
||||
ClientActive bool // debros-anyone-client systemd service active
|
||||
Mode string // "relay" or "client" (from anonrc ORPort presence)
|
||||
ORPortListening bool // port 9001 bound locally
|
||||
SocksListening bool // port 9050 bound locally (client SOCKS5)
|
||||
ControlListening bool // port 9051 bound locally (control port)
|
||||
Bootstrapped bool // relay has bootstrapped to 100%
|
||||
BootstrapPct int // bootstrap percentage (0-100)
|
||||
Fingerprint string // relay fingerprint
|
||||
Nickname string // relay nickname
|
||||
UptimeStr string // uptime from control port
|
||||
RelayActive bool // orama-anyone-relay systemd service active
|
||||
ClientActive bool // orama-anyone-client systemd service active
|
||||
Mode string // "relay" or "client" (from anonrc ORPort presence)
|
||||
ORPortListening bool // port 9001 bound locally
|
||||
SocksListening bool // port 9050 bound locally (client SOCKS5)
|
||||
ControlListening bool // port 9051 bound locally (control port)
|
||||
Bootstrapped bool // relay has bootstrapped to 100%
|
||||
BootstrapPct int // bootstrap percentage (0-100)
|
||||
Fingerprint string // relay fingerprint
|
||||
Nickname string // relay nickname
|
||||
UptimeStr string // uptime from control port
|
||||
ORPortReachable map[string]bool // host IP → whether we can TCP connect to their 9001 from this node
|
||||
}
|
||||
|
||||
@ -567,17 +567,17 @@ func collectOlric(ctx context.Context, node Node) *OlricData {
|
||||
cmd := `
|
||||
SEP="===INSPECTOR_SEP==="
|
||||
echo "$SEP"
|
||||
systemctl is-active debros-olric 2>/dev/null
|
||||
systemctl is-active orama-olric 2>/dev/null
|
||||
echo "$SEP"
|
||||
ss -tlnp 2>/dev/null | grep ':3322 ' | head -1
|
||||
echo "$SEP"
|
||||
journalctl -u debros-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(error|ERR)' || echo 0
|
||||
journalctl -u orama-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(error|ERR)' || echo 0
|
||||
echo "$SEP"
|
||||
journalctl -u debros-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(suspect|marking.*(failed|dead))' || echo 0
|
||||
journalctl -u orama-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(suspect|marking.*(failed|dead))' || echo 0
|
||||
echo "$SEP"
|
||||
journalctl -u debros-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(memberlist.*(join|leave))' || echo 0
|
||||
journalctl -u orama-olric --no-pager -n 200 --since "1 hour ago" 2>/dev/null | grep -ciE '(memberlist.*(join|leave))' || echo 0
|
||||
echo "$SEP"
|
||||
systemctl show debros-olric --property=NRestarts 2>/dev/null | cut -d= -f2
|
||||
systemctl show orama-olric --property=NRestarts 2>/dev/null | cut -d= -f2
|
||||
echo "$SEP"
|
||||
ps -C olric-server -o rss= 2>/dev/null | head -1 || echo 0
|
||||
`
|
||||
@ -611,9 +611,9 @@ func collectIPFS(ctx context.Context, node Node) *IPFSData {
|
||||
cmd := `
|
||||
SEP="===INSPECTOR_SEP==="
|
||||
echo "$SEP"
|
||||
systemctl is-active debros-ipfs 2>/dev/null
|
||||
systemctl is-active orama-ipfs 2>/dev/null
|
||||
echo "$SEP"
|
||||
systemctl is-active debros-ipfs-cluster 2>/dev/null
|
||||
systemctl is-active orama-ipfs-cluster 2>/dev/null
|
||||
echo "$SEP"
|
||||
curl -sf -X POST 'http://localhost:4501/api/v0/swarm/peers' 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('Peers') or []))" 2>/dev/null || echo -1
|
||||
echo "$SEP"
|
||||
@ -625,7 +625,7 @@ curl -sf -X POST 'http://localhost:4501/api/v0/version' 2>/dev/null | python3 -c
|
||||
echo "$SEP"
|
||||
curl -sf 'http://localhost:9094/id' 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('version',''))" 2>/dev/null || echo unknown
|
||||
echo "$SEP"
|
||||
sudo test -f /home/debros/.orama/data/ipfs/repo/swarm.key && echo yes || echo no
|
||||
sudo test -f /home/orama/.orama/data/ipfs/repo/swarm.key && echo yes || echo no
|
||||
echo "$SEP"
|
||||
curl -sf -X POST 'http://localhost:4501/api/v0/bootstrap/list' 2>/dev/null | python3 -c "import sys,json; peers=json.load(sys.stdin).get('Peers',[]); print(len(peers))" 2>/dev/null || echo -1
|
||||
`
|
||||
@ -887,8 +887,8 @@ func collectSystem(ctx context.Context, node Node) *SystemData {
|
||||
}
|
||||
|
||||
services := []string{
|
||||
"debros-node", "debros-ipfs", "debros-ipfs-cluster",
|
||||
"debros-olric", "debros-anyone-relay", "debros-anyone-client",
|
||||
"orama-node", "orama-ipfs", "orama-ipfs-cluster",
|
||||
"orama-olric", "orama-anyone-relay", "orama-anyone-client",
|
||||
"coredns", "caddy", "wg-quick@wg0",
|
||||
}
|
||||
|
||||
@ -918,9 +918,9 @@ func collectSystem(ctx context.Context, node Node) *SystemData {
|
||||
cmd += ` && echo "$SEP"`
|
||||
cmd += ` && sudo ufw status 2>/dev/null | head -1`
|
||||
cmd += ` && echo "$SEP"`
|
||||
cmd += ` && ps -C debros-node -o user= 2>/dev/null | head -1 || echo unknown`
|
||||
cmd += ` && ps -C orama-node -o user= 2>/dev/null | head -1 || echo unknown`
|
||||
cmd += ` && echo "$SEP"`
|
||||
cmd += ` && journalctl -u debros-node --no-pager -n 500 --since "1 hour ago" 2>/dev/null | grep -ciE '(panic|fatal)' || echo 0`
|
||||
cmd += ` && journalctl -u orama-node --no-pager -n 500 --since "1 hour ago" 2>/dev/null | grep -ciE '(panic|fatal)' || echo 0`
|
||||
|
||||
res := RunSSH(ctx, node, cmd)
|
||||
if !res.OK() && res.Stdout == "" {
|
||||
@ -1152,9 +1152,9 @@ func collectAnyone(ctx context.Context, node Node) *AnyoneData {
|
||||
cmd := `
|
||||
SEP="===INSPECTOR_SEP==="
|
||||
echo "$SEP"
|
||||
systemctl is-active debros-anyone-relay 2>/dev/null || echo inactive
|
||||
systemctl is-active orama-anyone-relay 2>/dev/null || echo inactive
|
||||
echo "$SEP"
|
||||
systemctl is-active debros-anyone-client 2>/dev/null || echo inactive
|
||||
systemctl is-active orama-anyone-client 2>/dev/null || echo inactive
|
||||
echo "$SEP"
|
||||
ss -tlnp 2>/dev/null | grep -q ':9001 ' && echo yes || echo no
|
||||
echo "$SEP"
|
||||
@ -1289,11 +1289,11 @@ func collectAnyoneReachability(ctx context.Context, data *ClusterData) {
|
||||
}
|
||||
|
||||
func collectNamespaces(ctx context.Context, node Node) []NamespaceData {
|
||||
// Detect namespace services: debros-namespace-gateway@<name>.service
|
||||
// Detect namespace services: orama-namespace-gateway@<name>.service
|
||||
cmd := `
|
||||
SEP="===INSPECTOR_SEP==="
|
||||
echo "$SEP"
|
||||
systemctl list-units --type=service --all --no-pager --no-legend 'debros-namespace-gateway@*.service' 2>/dev/null | awk '{print $1}' | sed 's/debros-namespace-gateway@//;s/\.service//'
|
||||
systemctl list-units --type=service --all --no-pager --no-legend 'orama-namespace-gateway@*.service' 2>/dev/null | awk '{print $1}' | sed 's/orama-namespace-gateway@//;s/\.service//'
|
||||
echo "$SEP"
|
||||
`
|
||||
res := RunSSH(ctx, node, cmd)
|
||||
@ -1327,7 +1327,7 @@ echo "$SEP"
|
||||
nsCmd += fmt.Sprintf(`
|
||||
echo "NS_START:%s"
|
||||
# Get gateway port from systemd or default discovery
|
||||
GWPORT=$(ss -tlnp 2>/dev/null | grep 'debros-namespace-gateway@%s' | grep -oP ':\K[0-9]+' | head -1)
|
||||
GWPORT=$(ss -tlnp 2>/dev/null | grep 'orama-namespace-gateway@%s' | grep -oP ':\K[0-9]+' | head -1)
|
||||
echo "GW_PORT:${GWPORT:-0}"
|
||||
# Try common namespace port ranges (10000-10099)
|
||||
for BASE in $(seq 10000 5 10099); do
|
||||
|
||||
@ -21,7 +21,7 @@ type DiscoveryResult struct {
|
||||
|
||||
// DiscoverPeerFromDomain queries an existing node to get its peer ID and IPFS info
|
||||
// Tries HTTPS first, then falls back to HTTP
|
||||
// Respects DEBROS_TRUSTED_TLS_DOMAINS and DEBROS_CA_CERT_PATH environment variables for certificate verification
|
||||
// Respects ORAMA_TRUSTED_TLS_DOMAINS and ORAMA_CA_CERT_PATH environment variables for certificate verification
|
||||
func DiscoverPeerFromDomain(domain string) (*DiscoveryResult, error) {
|
||||
// Use centralized TLS configuration that respects CA certificates and trusted domains
|
||||
client := tlsutil.NewHTTPClientForDomain(10*time.Second, domain)
|
||||
|
||||
@ -113,7 +113,7 @@ func (cm *ClusterConfigManager) EnsureConfig() error {
|
||||
cfg.Cluster.Peername = nodeName
|
||||
cfg.Cluster.Secret = cm.secret
|
||||
cfg.Cluster.ListenMultiaddress = []string{fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", clusterListenPort)}
|
||||
cfg.Consensus.CRDT.ClusterName = "debros-cluster"
|
||||
cfg.Consensus.CRDT.ClusterName = "orama-cluster"
|
||||
cfg.Consensus.CRDT.TrustedPeers = []string{"*"}
|
||||
cfg.API.RestAPI.HTTPListenMultiaddress = fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", restAPIPort)
|
||||
cfg.API.IPFSProxy.ListenMultiaddress = fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", proxyPort)
|
||||
|
||||
@ -86,9 +86,9 @@ func (is *InstanceSpawner) SpawnInstance(ctx context.Context, cfg InstanceConfig
|
||||
// Raft tuning — match the global node's tuning for consistency
|
||||
args = append(args,
|
||||
"-raft-election-timeout", "5s",
|
||||
"-raft-heartbeat-timeout", "2s",
|
||||
"-raft-timeout", "2s",
|
||||
"-raft-apply-timeout", "30s",
|
||||
"-raft-leader-lease-timeout", "5s",
|
||||
"-raft-leader-lease-timeout", "2s",
|
||||
)
|
||||
|
||||
// Add join addresses if not the leader (must be before data directory)
|
||||
|
||||
@ -128,11 +128,11 @@ func (r *RQLiteManager) launchProcess(ctx context.Context, rqliteDataDir string)
|
||||
}
|
||||
raftLeaderLease := r.config.RaftLeaderLeaseTimeout
|
||||
if raftLeaderLease == 0 {
|
||||
raftLeaderLease = 5 * time.Second
|
||||
raftLeaderLease = 2 * time.Second
|
||||
}
|
||||
args = append(args,
|
||||
"-raft-election-timeout", raftElection.String(),
|
||||
"-raft-heartbeat-timeout", raftHeartbeat.String(),
|
||||
"-raft-timeout", raftHeartbeat.String(),
|
||||
"-raft-apply-timeout", raftApply.String(),
|
||||
"-raft-leader-lease-timeout", raftLeaderLease.String(),
|
||||
)
|
||||
|
||||
@ -17,7 +17,7 @@ func (r *RQLiteManager) rqliteDataDirPath() (string, error) {
|
||||
}
|
||||
|
||||
func (r *RQLiteManager) resolveMigrationsDir() (string, error) {
|
||||
productionPath := "/home/debros/src/migrations"
|
||||
productionPath := "/home/orama/src/migrations"
|
||||
if _, err := os.Stat(productionPath); err == nil {
|
||||
return productionPath, nil
|
||||
}
|
||||
@ -55,4 +55,3 @@ func (r *RQLiteManager) exponentialBackoff(attempt int, baseDelay time.Duration,
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ func NewManager(namespaceBase string, logger *zap.Logger) *Manager {
|
||||
|
||||
// serviceName returns the systemd service name for a namespace and service type
|
||||
func (m *Manager) serviceName(namespace string, serviceType ServiceType) string {
|
||||
return fmt.Sprintf("debros-namespace-%s@%s.service", serviceType, namespace)
|
||||
return fmt.Sprintf("orama-namespace-%s@%s.service", serviceType, namespace)
|
||||
}
|
||||
|
||||
// StartService starts a namespace service
|
||||
@ -228,7 +228,7 @@ func (m *Manager) StartAllNamespaceServices(namespace string) error {
|
||||
|
||||
// ListNamespaceServices returns all namespace services currently registered in systemd
|
||||
func (m *Manager) ListNamespaceServices() ([]string, error) {
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "list-units", "--all", "--no-legend", "debros-namespace-*@*.service")
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "list-units", "--all", "--no-legend", "orama-namespace-*@*.service")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list namespace services: %w; output: %s", err, string(output))
|
||||
@ -343,9 +343,9 @@ func (m *Manager) InstallTemplateUnits(sourceDir string) error {
|
||||
m.logger.Info("Installing systemd template units", zap.String("source", sourceDir))
|
||||
|
||||
templates := []string{
|
||||
"debros-namespace-rqlite@.service",
|
||||
"debros-namespace-olric@.service",
|
||||
"debros-namespace-gateway@.service",
|
||||
"orama-namespace-rqlite@.service",
|
||||
"orama-namespace-olric@.service",
|
||||
"orama-namespace-gateway@.service",
|
||||
}
|
||||
|
||||
for _, template := range templates {
|
||||
|
||||
@ -29,7 +29,7 @@ func init() {
|
||||
trustedDomains = append(trustedDomains, defaultTrustedDomains...)
|
||||
|
||||
// Add any additional domains from environment
|
||||
domains := os.Getenv("DEBROS_TRUSTED_TLS_DOMAINS")
|
||||
domains := os.Getenv("ORAMA_TRUSTED_TLS_DOMAINS")
|
||||
if domains != "" {
|
||||
for _, d := range strings.Split(domains, ",") {
|
||||
d = strings.TrimSpace(d)
|
||||
@ -40,9 +40,9 @@ func init() {
|
||||
}
|
||||
|
||||
// Try to load CA certificate
|
||||
caCertPath := os.Getenv("DEBROS_CA_CERT_PATH")
|
||||
caCertPath := os.Getenv("ORAMA_CA_CERT_PATH")
|
||||
if caCertPath == "" {
|
||||
caCertPath = "/etc/debros/ca.crt"
|
||||
caCertPath = "/etc/orama/ca.crt"
|
||||
}
|
||||
|
||||
if caCertData, err := os.ReadFile(caCertPath); err == nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user