mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-17 01:54:13 +00:00
feat(#72): install ntfy on every node, drop --with-ntfy gating
ntfy is now part of the standard node install, just like Caddy. The binary, /etc/ntfy/server.yml, and the Caddy push.<dnsZone> reverse- proxy block are written unconditionally on every node, and the ntfy.service starts as part of the standard service order. Why uniform: ntfy listens on 127.0.0.1:NtfyListenPort only, reachable exclusively via the local Caddy reverse-proxy block. Nodes that don't serve a public push.* DNS entry just have an idle ntfy with no inbound traffic — zero operational cost, zero attack surface change. Removing the flag means no per-node toggling, no preference drift between nodes, no "did we remember to set --with-ntfy" mistakes when DNS topology changes (e.g. promoting a node to nameserver later). Removed: - NodePreferences.NtfyHost (yaml: ntfy_host) - ProductionSetup.isNtfyHost field, SetNtfyHost, IsNtfyHost - install/flags.go --with-ntfy + NtfyHost field - upgrade/flags.go --with-ntfy + NtfyHost field + isFlagPassed helper (was only used for --with-ntfy tri-state semantics) - upgrade/orchestrator.go preference-load and persist for ntfy - upgrade/remote.go --with-ntfy forwarding Phase 2 always calls InstallNtfy. Phase 4 always calls EnableCaddyNtfyProxy + ConfigureNtfy. Phase 5 always enables ntfy.service. Phase 5b always starts ntfy.service. VERSION bumped to 0.122.16.
This commit is contained in:
parent
8c37ef547e
commit
8b4abb7eef
@ -15,7 +15,6 @@ type Flags struct {
|
|||||||
DryRun bool
|
DryRun bool
|
||||||
SkipChecks bool
|
SkipChecks bool
|
||||||
Nameserver bool // Make this node a nameserver (runs CoreDNS + Caddy)
|
Nameserver bool // Make this node a nameserver (runs CoreDNS + Caddy)
|
||||||
NtfyHost bool // Host the self-hosted ntfy server on this node (feature #72)
|
|
||||||
JoinAddress string // HTTPS URL of existing node (e.g., https://node1.dbrs.space)
|
JoinAddress string // HTTPS URL of existing node (e.g., https://node1.dbrs.space)
|
||||||
Token string // Invite token for joining (from orama invite)
|
Token string // Invite token for joining (from orama invite)
|
||||||
ClusterSecret string // Deprecated: use --token instead
|
ClusterSecret string // Deprecated: use --token instead
|
||||||
@ -65,7 +64,6 @@ func ParseFlags(args []string) (*Flags, error) {
|
|||||||
fs.BoolVar(&flags.DryRun, "dry-run", false, "Show what would be done without making changes")
|
fs.BoolVar(&flags.DryRun, "dry-run", false, "Show what would be done without making changes")
|
||||||
fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)")
|
fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)")
|
||||||
fs.BoolVar(&flags.Nameserver, "nameserver", false, "Make this node a nameserver (runs CoreDNS + Caddy)")
|
fs.BoolVar(&flags.Nameserver, "nameserver", false, "Make this node a nameserver (runs CoreDNS + Caddy)")
|
||||||
fs.BoolVar(&flags.NtfyHost, "with-ntfy", false, "Host the self-hosted ntfy server (feature #72; usually colocated with --nameserver on devnet)")
|
|
||||||
|
|
||||||
// Cluster join flags
|
// Cluster join flags
|
||||||
fs.StringVar(&flags.JoinAddress, "join", "", "Join existing cluster via HTTPS URL (e.g. https://node1.dbrs.space)")
|
fs.StringVar(&flags.JoinAddress, "join", "", "Join existing cluster via HTTPS URL (e.g. https://node1.dbrs.space)")
|
||||||
|
|||||||
@ -46,7 +46,6 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) {
|
|||||||
|
|
||||||
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
|
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
|
||||||
setup.SetNameserver(flags.Nameserver)
|
setup.SetNameserver(flags.Nameserver)
|
||||||
setup.SetNtfyHost(flags.NtfyHost)
|
|
||||||
|
|
||||||
// Configure Anyone mode
|
// Configure Anyone mode
|
||||||
if flags.AnyoneRelay && flags.AnyoneClient {
|
if flags.AnyoneRelay && flags.AnyoneClient {
|
||||||
|
|||||||
@ -12,7 +12,6 @@ type Flags struct {
|
|||||||
RestartServices bool
|
RestartServices bool
|
||||||
SkipChecks bool
|
SkipChecks bool
|
||||||
Nameserver *bool // Pointer so we can detect if explicitly set vs default
|
Nameserver *bool // Pointer so we can detect if explicitly set vs default
|
||||||
NtfyHost *bool // Feature #72: nil = use saved preference; non-nil = explicit override
|
|
||||||
|
|
||||||
// Remote upgrade flags
|
// Remote upgrade flags
|
||||||
Env string // Target environment for remote rolling upgrade
|
Env string // Target environment for remote rolling upgrade
|
||||||
@ -51,8 +50,6 @@ func ParseFlags(args []string) (*Flags, error) {
|
|||||||
|
|
||||||
// Nameserver flag - use pointer to detect if explicitly set
|
// Nameserver flag - use pointer to detect if explicitly set
|
||||||
nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)")
|
nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)")
|
||||||
// Ntfy host flag (feature #72) — same pattern, sticks via preferences.yaml.
|
|
||||||
ntfyHost := fs.Bool("with-ntfy", false, "Host the self-hosted ntfy server on this node (uses saved preference if not specified)")
|
|
||||||
|
|
||||||
// Anyone flags
|
// Anyone flags
|
||||||
fs.BoolVar(&flags.AnyoneClient, "anyone-client", false, "Install Anyone as client-only (SOCKS5 proxy on port 9050, no relay)")
|
fs.BoolVar(&flags.AnyoneClient, "anyone-client", false, "Install Anyone as client-only (SOCKS5 proxy on port 9050, no relay)")
|
||||||
@ -78,25 +75,7 @@ func ParseFlags(args []string) (*Flags, error) {
|
|||||||
if *nameserver {
|
if *nameserver {
|
||||||
flags.Nameserver = nameserver
|
flags.Nameserver = nameserver
|
||||||
}
|
}
|
||||||
// Set ntfy_host only when explicitly passed (default false != "use saved").
|
|
||||||
// Without explicit set, the orchestrator reads the saved preference.
|
|
||||||
if isFlagPassed(fs, "with-ntfy") {
|
|
||||||
flags.NtfyHost = ntfyHost
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags, nil
|
return flags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFlagPassed reports whether the named flag was explicitly set on
|
|
||||||
// the command line, not just defaulted. Used to distinguish "user
|
|
||||||
// didn't say anything; honor saved preference" from "user wrote
|
|
||||||
// --with-ntfy=false; turn it OFF".
|
|
||||||
func isFlagPassed(fs *flag.FlagSet, name string) bool {
|
|
||||||
passed := false
|
|
||||||
fs.Visit(func(f *flag.Flag) {
|
|
||||||
if f.Name == name {
|
|
||||||
passed = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return passed
|
|
||||||
}
|
|
||||||
|
|||||||
@ -38,17 +38,8 @@ func NewOrchestrator(flags *Flags) *Orchestrator {
|
|||||||
isNameserver = *flags.Nameserver
|
isNameserver = *flags.Nameserver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Feature #72: ntfy-host preference survives upgrades the same way
|
|
||||||
// nameserver does — loaded from preferences.yaml unless the upgrade
|
|
||||||
// flags override it explicitly.
|
|
||||||
isNtfyHost := prefs.NtfyHost
|
|
||||||
if flags.NtfyHost != nil {
|
|
||||||
isNtfyHost = *flags.NtfyHost
|
|
||||||
}
|
|
||||||
|
|
||||||
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
|
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
|
||||||
setup.SetNameserver(isNameserver)
|
setup.SetNameserver(isNameserver)
|
||||||
setup.SetNtfyHost(isNtfyHost)
|
|
||||||
|
|
||||||
// Configure Anyone mode (explicit flags > saved preferences > auto-detect)
|
// Configure Anyone mode (explicit flags > saved preferences > auto-detect)
|
||||||
// Explicit flags always win — they represent the user's current intent.
|
// Explicit flags always win — they represent the user's current intent.
|
||||||
@ -220,15 +211,6 @@ func (o *Orchestrator) handleBranchPreferences() error {
|
|||||||
fmt.Printf(" Nameserver mode: enabled (CoreDNS + Caddy)\n")
|
fmt.Printf(" Nameserver mode: enabled (CoreDNS + Caddy)\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If ntfy-host was explicitly provided, persist it (feature #72).
|
|
||||||
if o.flags.NtfyHost != nil {
|
|
||||||
prefs.NtfyHost = *o.flags.NtfyHost
|
|
||||||
prefsChanged = true
|
|
||||||
}
|
|
||||||
if o.setup.IsNtfyHost() {
|
|
||||||
fmt.Printf(" ntfy host: enabled (self-hosted ntfy on push.<dnsZone>)\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anyone client and relay are mutually exclusive — setting one clears the other.
|
// Anyone client and relay are mutually exclusive — setting one clears the other.
|
||||||
if o.flags.AnyoneClient {
|
if o.flags.AnyoneClient {
|
||||||
prefs.AnyoneClient = true
|
prefs.AnyoneClient = true
|
||||||
|
|||||||
@ -68,24 +68,16 @@ func (r *RemoteUpgrader) Execute() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// upgradeNode runs `orama node upgrade --restart` on a single remote node,
|
// upgradeNode runs `orama node upgrade --restart` on a single remote node,
|
||||||
// forwarding the per-node flags the operator passed locally (--with-ntfy,
|
// forwarding the per-node flags the operator passed locally (--nameserver,
|
||||||
// --nameserver, --force, --skip-checks) so the remote orchestrator sees the
|
// --force, --skip-checks) so the remote orchestrator sees the same intent.
|
||||||
// same intent. Without this forwarding, the remote command would always use
|
// Without this forwarding, the remote command would always use the saved
|
||||||
// the saved preference, silently dropping operator overrides like
|
// preference, silently dropping operator overrides on the floor.
|
||||||
// `--with-ntfy` on the floor.
|
|
||||||
func (r *RemoteUpgrader) upgradeNode(node inspector.Node) error {
|
func (r *RemoteUpgrader) upgradeNode(node inspector.Node) error {
|
||||||
sudo := remotessh.SudoPrefix(node)
|
sudo := remotessh.SudoPrefix(node)
|
||||||
cmd := fmt.Sprintf("%sorama node upgrade --restart", sudo)
|
cmd := fmt.Sprintf("%sorama node upgrade --restart", sudo)
|
||||||
|
|
||||||
// Tri-state pointer flags: forward only when explicitly set locally.
|
// Tri-state pointer flag: forward only when explicitly set locally.
|
||||||
// nil = "honor saved preference on the remote" — don't pass anything.
|
// nil = "honor saved preference on the remote" — don't pass anything.
|
||||||
if r.flags.NtfyHost != nil {
|
|
||||||
if *r.flags.NtfyHost {
|
|
||||||
cmd += " --with-ntfy"
|
|
||||||
} else {
|
|
||||||
cmd += " --with-ntfy=false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.flags.Nameserver != nil {
|
if r.flags.Nameserver != nil {
|
||||||
if *r.flags.Nameserver {
|
if *r.flags.Nameserver {
|
||||||
cmd += " --nameserver"
|
cmd += " --nameserver"
|
||||||
|
|||||||
@ -38,7 +38,6 @@ type ProductionSetup struct {
|
|||||||
skipOptionalDeps bool
|
skipOptionalDeps bool
|
||||||
skipResourceChecks 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)
|
||||||
isNtfyHost bool // Feature #72: whether this node hosts the self-hosted ntfy server
|
|
||||||
isAnyoneClient bool // Whether this node runs Anyone as client-only (SOCKS5 proxy)
|
isAnyoneClient bool // Whether this node runs Anyone as client-only (SOCKS5 proxy)
|
||||||
anyoneRelayConfig *AnyoneRelayConfig // Configuration for Anyone relay mode
|
anyoneRelayConfig *AnyoneRelayConfig // Configuration for Anyone relay mode
|
||||||
privChecker *PrivilegeChecker
|
privChecker *PrivilegeChecker
|
||||||
@ -136,20 +135,6 @@ func (ps *ProductionSetup) IsNameserver() bool {
|
|||||||
return ps.isNameserver
|
return ps.isNameserver
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNtfyHost flags this node as the host for the self-hosted ntfy
|
|
||||||
// server (feature #72). When set, Phase 2 installs ntfy and Phase 4
|
|
||||||
// generates /etc/ntfy/server.yml plus a Caddy reverse-proxy block for
|
|
||||||
// push.<dnsZone>. Requires isNameserver=true for devnet (ns1 also
|
|
||||||
// runs Caddy); production deployments may colocate or split.
|
|
||||||
func (ps *ProductionSetup) SetNtfyHost(isNtfy bool) {
|
|
||||||
ps.isNtfyHost = isNtfy
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNtfyHost returns whether this node hosts ntfy.
|
|
||||||
func (ps *ProductionSetup) IsNtfyHost() bool {
|
|
||||||
return ps.isNtfyHost
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAnyoneRelayConfig sets the Anyone relay configuration
|
// SetAnyoneRelayConfig sets the Anyone relay configuration
|
||||||
func (ps *ProductionSetup) SetAnyoneRelayConfig(config *AnyoneRelayConfig) {
|
func (ps *ProductionSetup) SetAnyoneRelayConfig(config *AnyoneRelayConfig) {
|
||||||
ps.anyoneRelayConfig = config
|
ps.anyoneRelayConfig = config
|
||||||
@ -359,13 +344,15 @@ func (ps *ProductionSetup) installFromSource() error {
|
|||||||
ps.logf(" ⚠️ Caddy install warning: %v", err)
|
ps.logf(" ⚠️ Caddy install warning: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install ntfy only on nodes flagged as the ntfy host (feature #72).
|
// Install ntfy on every node (feature #72). ntfy listens on
|
||||||
// On devnet this is ns1; on production it can be a dedicated node.
|
// 127.0.0.1:NtfyListenPort and is only reachable via the local
|
||||||
if ps.isNtfyHost {
|
// Caddy reverse-proxy block, so it's safe to run cluster-wide:
|
||||||
|
// nodes that don't host a public push.* DNS entry simply have
|
||||||
|
// an idle ntfy with no inbound traffic. Uniform install means no
|
||||||
|
// per-node toggling and no surprises when DNS topology changes.
|
||||||
if err := ps.binaryInstaller.InstallNtfy(); err != nil {
|
if err := ps.binaryInstaller.InstallNtfy(); err != nil {
|
||||||
ps.logf(" ⚠️ ntfy install warning: %v", err)
|
ps.logf(" ⚠️ ntfy install warning: %v", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// These are pre-built binary downloads (not Go compilation), always run them
|
// These are pre-built binary downloads (not Go compilation), always run them
|
||||||
if err := ps.binaryInstaller.InstallRQLite(); err != nil {
|
if err := ps.binaryInstaller.InstallRQLite(); err != nil {
|
||||||
@ -725,12 +712,12 @@ func (ps *ProductionSetup) Phase4GenerateConfigs(peerAddresses []string, vpsIP s
|
|||||||
email := "admin@" + caddyDomain
|
email := "admin@" + caddyDomain
|
||||||
acmeEndpoint := "http://localhost:6001/v1/internal/acme"
|
acmeEndpoint := "http://localhost:6001/v1/internal/acme"
|
||||||
|
|
||||||
// Self-hosted ntfy (feature #72): when this node hosts ntfy,
|
// Self-hosted ntfy (feature #72): always emit the Caddy
|
||||||
// (a) tell the Caddy installer to emit a push.<dnsZone> block
|
// push.<dnsZone> reverse-proxy block and write
|
||||||
// pointing at the local ntfy listen port, and (b) write the
|
// /etc/ntfy/server.yml. Must happen BEFORE ConfigureCaddy is
|
||||||
// ntfy server.yml. Both must happen BEFORE ConfigureCaddy is
|
|
||||||
// called below so the generated Caddyfile picks up the block.
|
// called below so the generated Caddyfile picks up the block.
|
||||||
if ps.isNtfyHost {
|
// ntfy is installed unconditionally on every node (see Phase 2)
|
||||||
|
// so the local 127.0.0.1:NtfyListenPort target always exists.
|
||||||
ntfyHost := "push." + dnsZone
|
ntfyHost := "push." + dnsZone
|
||||||
ps.binaryInstaller.EnableCaddyNtfyProxy(ntfyHost)
|
ps.binaryInstaller.EnableCaddyNtfyProxy(ntfyHost)
|
||||||
ntfyBaseURL := "https://" + ntfyHost
|
ntfyBaseURL := "https://" + ntfyHost
|
||||||
@ -739,7 +726,6 @@ func (ps *ProductionSetup) Phase4GenerateConfigs(peerAddresses []string, vpsIP s
|
|||||||
} else {
|
} else {
|
||||||
ps.logf(" ✓ ntfy config generated (base_url: %s)", ntfyBaseURL)
|
ps.logf(" ✓ ntfy config generated (base_url: %s)", ntfyBaseURL)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := ps.binaryInstaller.ConfigureCaddy(caddyDomain, email, acmeEndpoint, baseDomain); err != nil {
|
if err := ps.binaryInstaller.ConfigureCaddy(caddyDomain, email, acmeEndpoint, baseDomain); err != nil {
|
||||||
ps.logf(" ⚠️ Caddy config warning: %v", err)
|
ps.logf(" ⚠️ Caddy config warning: %v", err)
|
||||||
@ -899,13 +885,11 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
|||||||
if _, err := os.Stat("/usr/bin/caddy"); err == nil {
|
if _, err := os.Stat("/usr/bin/caddy"); err == nil {
|
||||||
services = append(services, "caddy.service")
|
services = append(services, "caddy.service")
|
||||||
}
|
}
|
||||||
// Add ntfy when this node hosts the self-hosted ntfy server (#72).
|
// Add ntfy on every node (#72). The unit file is written by
|
||||||
// The unit file is written by installers/ntfy.go::writeSystemdUnit.
|
// installers/ntfy.go::writeSystemdUnit during Phase 2.
|
||||||
if ps.isNtfyHost {
|
|
||||||
if _, err := os.Stat("/usr/local/bin/ntfy"); err == nil {
|
if _, err := os.Stat("/usr/local/bin/ntfy"); err == nil {
|
||||||
services = append(services, "ntfy.service")
|
services = append(services, "ntfy.service")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for _, svc := range services {
|
for _, svc := range services {
|
||||||
if err := ps.serviceController.EnableService(svc); err != nil {
|
if err := ps.serviceController.EnableService(svc); err != nil {
|
||||||
ps.logf(" ⚠️ Failed to enable %s: %v", svc, err)
|
ps.logf(" ⚠️ Failed to enable %s: %v", svc, err)
|
||||||
@ -982,10 +966,9 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start ntfy when this node hosts it (#72). Caddy must already be
|
// Start ntfy on every node (#72). Caddy must already be up (it
|
||||||
// up (it terminates TLS for push.<dnsZone>), which the order
|
// terminates TLS for push.<dnsZone>), which the order above
|
||||||
// above guarantees.
|
// guarantees.
|
||||||
if ps.isNtfyHost {
|
|
||||||
if _, err := os.Stat("/usr/local/bin/ntfy"); err == nil {
|
if _, err := os.Stat("/usr/local/bin/ntfy"); err == nil {
|
||||||
if err := ps.serviceController.RestartService("ntfy.service"); err != nil {
|
if err := ps.serviceController.RestartService("ntfy.service"); err != nil {
|
||||||
ps.logf(" ⚠️ Failed to start ntfy.service: %v", err)
|
ps.logf(" ⚠️ Failed to start ntfy.service: %v", err)
|
||||||
@ -993,7 +976,6 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
|||||||
ps.logf(" - ntfy.service started")
|
ps.logf(" - ntfy.service started")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ps.logf(" ✓ All services started")
|
ps.logf(" ✓ All services started")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import (
|
|||||||
type NodePreferences struct {
|
type NodePreferences struct {
|
||||||
Branch string `yaml:"branch"`
|
Branch string `yaml:"branch"`
|
||||||
Nameserver bool `yaml:"nameserver"`
|
Nameserver bool `yaml:"nameserver"`
|
||||||
NtfyHost bool `yaml:"ntfy_host"` // Feature #72: this node hosts self-hosted ntfy
|
|
||||||
AnyoneClient bool `yaml:"anyone_client"`
|
AnyoneClient bool `yaml:"anyone_client"`
|
||||||
AnyoneRelay bool `yaml:"anyone_relay"`
|
AnyoneRelay bool `yaml:"anyone_relay"`
|
||||||
AnyoneORPort int `yaml:"anyone_orport,omitempty"` // typically 9001
|
AnyoneORPort int `yaml:"anyone_orport,omitempty"` // typically 9001
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@debros/orama",
|
"name": "@debros/orama",
|
||||||
"version": "0.122.15",
|
"version": "0.122.16",
|
||||||
"description": "TypeScript SDK for Orama Network - Database, PubSub, Cache, Storage, Vault, and more",
|
"description": "TypeScript SDK for Orama Network - Database, PubSub, Cache, Storage, Vault, and more",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user