Add ORLY_POLICY_PATH for custom policy file location
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
- Add ORLY_POLICY_PATH environment variable to configure custom policy file path, overriding the default ~/.config/ORLY/policy.json location - Enforce ABSOLUTE paths only - relay panics on startup if relative path is provided, preventing common misconfiguration errors - Update PolicyManager to store and expose configPath for hot-reload saves - Add ConfigPath() method to P struct delegating to internal PolicyManager - Update NewWithManager() signature to accept optional custom path parameter - Add BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md with issue submission guidelines requiring environment details, reproduction steps, and logs - Update README.md with system requirements (500MB minimum memory) and link to bug report protocol - Update CLAUDE.md and README.md documentation for new ORLY_POLICY_PATH Files modified: - app/config/config.go: Add PolicyPath config field - pkg/policy/policy.go: Add configPath storage and validation - app/handle-policy-config.go: Use policyManager.ConfigPath() - app/main.go: Pass cfg.PolicyPath to NewWithManager - pkg/policy/*_test.go: Update test calls with new parameter - BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md: New file - README.md, CLAUDE.md: Documentation updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,7 +116,8 @@
|
||||
"WebFetch(domain:eylenburg.github.io)",
|
||||
"Bash(go run -exec '' -c 'package main; import \"\"git.mleku.dev/mleku/nostr/utils/normalize\"\"; import \"\"fmt\"\"; func main() { fmt.Println(string(normalize.URL([]byte(\"\"relay.example.com:3334\"\")))); fmt.Println(string(normalize.URL([]byte(\"\"relay.example.com:443\"\")))); fmt.Println(string(normalize.URL([]byte(\"\"ws://relay.example.com:3334\"\")))); fmt.Println(string(normalize.URL([]byte(\"\"wss://relay.example.com:3334\"\")))) }')",
|
||||
"Bash(go run:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nFix NIP-11 fetch URL scheme conversion for non-proxied relays\n\n- Convert wss:// to https:// and ws:// to http:// before fetching NIP-11\n documents, fixing failures for users not using HTTPS upgrade proxies\n- The fetchNIP11 function was using WebSocket URLs directly for HTTP\n requests, causing scheme mismatch errors\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n)\")"
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nFix NIP-11 fetch URL scheme conversion for non-proxied relays\n\n- Convert wss:// to https:// and ws:// to http:// before fetching NIP-11\n documents, fixing failures for users not using HTTPS upgrade proxies\n- The fetchNIP11 function was using WebSocket URLs directly for HTTP\n requests, causing scheme mismatch errors\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(/tmp/orly help:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
254
BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md
Normal file
254
BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# Feature Request and Bug Report Protocol
|
||||
|
||||
This document describes how to submit effective bug reports and feature requests for ORLY relay. Following these guidelines helps maintainers understand and resolve issues quickly.
|
||||
|
||||
## Before Submitting
|
||||
|
||||
1. **Search existing issues** - Your issue may already be reported or discussed
|
||||
2. **Check documentation** - Review `CLAUDE.md`, `docs/`, and `pkg/*/README.md` files
|
||||
3. **Verify with latest version** - Ensure the issue exists in the current release
|
||||
4. **Test with default configuration** - Rule out configuration-specific problems
|
||||
|
||||
## Bug Reports
|
||||
|
||||
### Required Information
|
||||
|
||||
**Title**: Concise summary of the problem
|
||||
- Good: "Kind 3 events with 8000+ follows truncated on save"
|
||||
- Bad: "Events not saving" or "Bug in database"
|
||||
|
||||
**Environment**:
|
||||
```
|
||||
ORLY version: (output of ./orly version)
|
||||
OS: (e.g., Ubuntu 24.04, macOS 14.2)
|
||||
Go version: (output of go version)
|
||||
Database backend: (badger/neo4j/wasmdb)
|
||||
```
|
||||
|
||||
**Configuration** (relevant settings only):
|
||||
```bash
|
||||
ORLY_DB_TYPE=badger
|
||||
ORLY_POLICY_ENABLED=true
|
||||
# Include any non-default settings
|
||||
```
|
||||
|
||||
**Steps to Reproduce**:
|
||||
1. Start relay with configuration X
|
||||
2. Connect client and send event Y
|
||||
3. Query for event with filter Z
|
||||
4. Observe error/unexpected behavior
|
||||
|
||||
**Expected Behavior**: What should happen
|
||||
|
||||
**Actual Behavior**: What actually happens
|
||||
|
||||
**Logs**: Include relevant log output with `ORLY_LOG_LEVEL=debug` or `trace`
|
||||
|
||||
### Minimal Reproduction
|
||||
|
||||
The most effective bug reports include a minimal reproduction case:
|
||||
|
||||
```bash
|
||||
# Example: Script that demonstrates the issue
|
||||
export ORLY_LOG_LEVEL=debug
|
||||
./orly &
|
||||
sleep 2
|
||||
|
||||
# Send problematic event
|
||||
echo '["EVENT", {...}]' | websocat ws://localhost:3334
|
||||
|
||||
# Show the failure
|
||||
echo '["REQ", "test", {"kinds": [1]}]' | websocat ws://localhost:3334
|
||||
```
|
||||
|
||||
Or provide a failing test case:
|
||||
|
||||
```go
|
||||
func TestReproduceBug(t *testing.T) {
|
||||
// Setup
|
||||
db := setupTestDB(t)
|
||||
|
||||
// This should work but fails
|
||||
event := createTestEvent(kind, content)
|
||||
err := db.SaveEvent(ctx, event)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Query returns unexpected result
|
||||
results, err := db.QueryEvents(ctx, filter)
|
||||
assert.Len(t, results, 1) // Fails: got 0
|
||||
}
|
||||
```
|
||||
|
||||
## Feature Requests
|
||||
|
||||
### Required Information
|
||||
|
||||
**Title**: Clear description of the feature
|
||||
- Good: "Add WebSocket compression support (permessage-deflate)"
|
||||
- Bad: "Make it faster" or "New feature idea"
|
||||
|
||||
**Problem Statement**: What problem does this solve?
|
||||
```
|
||||
Currently, clients with high-latency connections experience slow sync times
|
||||
because event data is transmitted uncompressed. A typical session transfers
|
||||
50MB of JSON that could be reduced to ~10MB with compression.
|
||||
```
|
||||
|
||||
**Proposed Solution**: How should it work?
|
||||
```
|
||||
Add optional permessage-deflate WebSocket extension support:
|
||||
- New config: ORLY_WS_COMPRESSION=true
|
||||
- Negotiate compression during WebSocket handshake
|
||||
- Apply to messages over configurable threshold (default 1KB)
|
||||
```
|
||||
|
||||
**Use Case**: Who benefits and how?
|
||||
```
|
||||
- Mobile clients on cellular connections
|
||||
- Users syncing large follow lists
|
||||
- Relays with bandwidth constraints
|
||||
```
|
||||
|
||||
**Alternatives Considered** (optional):
|
||||
```
|
||||
- Application-level compression: Rejected because it requires client changes
|
||||
- HTTP/2: Not applicable for WebSocket connections
|
||||
```
|
||||
|
||||
### Implementation Notes (optional)
|
||||
|
||||
If you have implementation ideas:
|
||||
|
||||
```
|
||||
Suggested approach:
|
||||
1. Add compression config to app/config/config.go
|
||||
2. Modify gorilla/websocket upgrader in app/handle-websocket.go
|
||||
3. Add compression threshold check before WriteMessage()
|
||||
|
||||
Reference: gorilla/websocket has built-in permessage-deflate support
|
||||
```
|
||||
|
||||
## What Makes Reports Effective
|
||||
|
||||
**Do**:
|
||||
- Be specific and factual
|
||||
- Include version numbers and exact error messages
|
||||
- Provide reproducible steps
|
||||
- Attach relevant logs (redact sensitive data)
|
||||
- Link to related issues or discussions
|
||||
- Respond to follow-up questions promptly
|
||||
|
||||
**Avoid**:
|
||||
- Vague descriptions ("it doesn't work")
|
||||
- Multiple unrelated issues in one report
|
||||
- Assuming the cause without evidence
|
||||
- Demanding immediate fixes
|
||||
- Duplicating existing issues
|
||||
|
||||
## Issue Labels
|
||||
|
||||
When applicable, suggest appropriate labels:
|
||||
|
||||
| Label | Use When |
|
||||
|-------|----------|
|
||||
| `bug` | Something isn't working as documented |
|
||||
| `enhancement` | New feature or improvement |
|
||||
| `performance` | Speed or resource usage issue |
|
||||
| `documentation` | Docs are missing or incorrect |
|
||||
| `question` | Clarification needed (not a bug) |
|
||||
| `good first issue` | Suitable for new contributors |
|
||||
|
||||
## Response Expectations
|
||||
|
||||
- **Acknowledgment**: Within a few days
|
||||
- **Triage**: Issue labeled and prioritized
|
||||
- **Resolution**: Depends on complexity and priority
|
||||
|
||||
Complex features may require discussion before implementation. Bug fixes for critical issues are prioritized.
|
||||
|
||||
## Following Up
|
||||
|
||||
If your issue hasn't received attention:
|
||||
|
||||
1. **Check issue status** - It may be labeled or assigned
|
||||
2. **Add new information** - If you've discovered more details
|
||||
3. **Politely bump** - A single follow-up comment after 2 weeks is appropriate
|
||||
4. **Consider contributing** - PRs that fix bugs or implement features are welcome
|
||||
|
||||
## Contributing Fixes
|
||||
|
||||
If you want to fix a bug or implement a feature yourself:
|
||||
|
||||
1. Comment on the issue to avoid duplicate work
|
||||
2. Follow the coding patterns in `CLAUDE.md`
|
||||
3. Include tests for your changes
|
||||
4. Keep PRs focused on a single issue
|
||||
5. Reference the issue number in your PR
|
||||
|
||||
## Security Issues
|
||||
|
||||
**Do not report security vulnerabilities in public issues.**
|
||||
|
||||
For security-sensitive bugs:
|
||||
- Contact maintainers directly
|
||||
- Provide detailed reproduction steps privately
|
||||
- Allow reasonable time for a fix before disclosure
|
||||
|
||||
## Examples
|
||||
|
||||
### Good Bug Report
|
||||
|
||||
```markdown
|
||||
## WebSocket disconnects after 60 seconds of inactivity
|
||||
|
||||
**Environment**:
|
||||
- ORLY v0.34.5
|
||||
- Ubuntu 22.04
|
||||
- Go 1.25.3
|
||||
- Badger backend
|
||||
|
||||
**Steps to Reproduce**:
|
||||
1. Connect to relay: `websocat ws://localhost:3334`
|
||||
2. Send subscription: `["REQ", "test", {"kinds": [1], "limit": 1}]`
|
||||
3. Wait 60 seconds without sending messages
|
||||
4. Observe connection closed
|
||||
|
||||
**Expected**: Connection remains open (Nostr relays should maintain persistent connections)
|
||||
|
||||
**Actual**: Connection closed with code 1000 after exactly 60 seconds
|
||||
|
||||
**Logs** (ORLY_LOG_LEVEL=debug):
|
||||
```
|
||||
1764783029014485🔎 client timeout, closing connection /app/handle-websocket.go:142
|
||||
```
|
||||
|
||||
**Possible Cause**: May be related to read deadline not being extended on subscription activity
|
||||
```
|
||||
|
||||
### Good Feature Request
|
||||
|
||||
```markdown
|
||||
## Add rate limiting per pubkey
|
||||
|
||||
**Problem**:
|
||||
A single pubkey can flood the relay with events, consuming storage and
|
||||
bandwidth. Currently there's no way to limit per-author submission rate.
|
||||
|
||||
**Proposed Solution**:
|
||||
Add configurable rate limiting:
|
||||
```bash
|
||||
ORLY_RATE_LIMIT_EVENTS_PER_MINUTE=60
|
||||
ORLY_RATE_LIMIT_BURST=10
|
||||
```
|
||||
|
||||
When exceeded, return OK false with "rate-limited" message per NIP-20.
|
||||
|
||||
**Use Case**:
|
||||
- Public relays protecting against spam
|
||||
- Community relays with fair-use policies
|
||||
- Paid relays enforcing subscription tiers
|
||||
|
||||
**Alternatives Considered**:
|
||||
- IP-based limiting: Ineffective because users share IPs and use VPNs
|
||||
- Global limiting: Punishes all users for one bad actor
|
||||
```
|
||||
@@ -147,6 +147,10 @@ export ORLY_SPROCKET_ENABLED=true
|
||||
# Enable policy system
|
||||
export ORLY_POLICY_ENABLED=true
|
||||
|
||||
# Custom policy file path (MUST be ABSOLUTE path starting with /)
|
||||
# Default: ~/.config/ORLY/policy.json (or ~/.config/{ORLY_APP_NAME}/policy.json)
|
||||
# export ORLY_POLICY_PATH=/etc/orly/policy.json
|
||||
|
||||
# Database backend selection (badger, neo4j, or wasmdb)
|
||||
export ORLY_DB_TYPE=badger
|
||||
|
||||
@@ -270,7 +274,8 @@ export ORLY_AUTH_TO_WRITE=false # Require auth only for writes
|
||||
- `none.go` - Open relay (no restrictions)
|
||||
|
||||
**`pkg/policy/`** - Event filtering and validation policies
|
||||
- Policy configuration loaded from `~/.config/ORLY/policy.json`
|
||||
- Policy configuration loaded from `~/.config/ORLY/policy.json` by default
|
||||
- Custom path via `ORLY_POLICY_PATH` (MUST be absolute path starting with `/`)
|
||||
- Per-kind size limits, age restrictions, custom scripts
|
||||
- **Write-Only Validation**: Size, age, tag, and expiry validations apply ONLY to write operations
|
||||
- **Read-Only Filtering**: `read_allow`, `read_deny`, `privileged` apply ONLY to read operations
|
||||
|
||||
22
README.md
22
README.md
@@ -1,5 +1,7 @@
|
||||
# next.orly.dev
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -10,6 +12,19 @@ zap me: <20>mlekudev@getalby.com
|
||||
|
||||
follow me on [nostr](https://jumble.social/users/npub1fjqqy4a93z5zsjwsfxqhc2764kvykfdyttvldkkkdera8dr78vhsmmleku)
|
||||
|
||||
## ⚠️ Bug Reports & Feature Requests
|
||||
|
||||
**Bug reports and feature requests that do not follow the protocol will not be accepted.**
|
||||
|
||||
Before submitting any issue, you must read and follow [BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md](./BUG_REPORTS_AND_FEATURE_REQUEST_PROTOCOL.md).
|
||||
|
||||
Requirements:
|
||||
- **Bug reports**: Include environment details, reproduction steps, expected/actual behavior, and logs
|
||||
- **Feature requests**: Include problem statement, proposed solution, and use cases
|
||||
- **Both**: Search existing issues first, verify with latest version, provide minimal reproduction
|
||||
|
||||
Issues missing required information will be closed without review.
|
||||
|
||||
## ⚠️ System Requirements
|
||||
|
||||
> **IMPORTANT: ORLY requires a minimum of 500MB of free memory to operate.**
|
||||
@@ -217,7 +232,12 @@ ORLY includes a comprehensive policy system for fine-grained control over event
|
||||
|
||||
```bash
|
||||
export ORLY_POLICY_ENABLED=true
|
||||
# Create policy file at ~/.config/ORLY/policy.json
|
||||
# Default policy file: ~/.config/ORLY/policy.json
|
||||
|
||||
# OPTIONAL: Use a custom policy file location
|
||||
# WARNING: ORLY_POLICY_PATH MUST be an ABSOLUTE path (starting with /)
|
||||
# Relative paths will be REJECTED and the relay will fail to start
|
||||
export ORLY_POLICY_PATH=/etc/orly/policy.json
|
||||
```
|
||||
|
||||
For detailed configuration and examples, see the [Policy Usage Guide](docs/POLICY_USAGE_GUIDE.md).
|
||||
|
||||
@@ -82,7 +82,8 @@ type C struct {
|
||||
DirectorySpiderInterval time.Duration `env:"ORLY_DIRECTORY_SPIDER_INTERVAL" default:"24h" usage:"how often to run directory spider"`
|
||||
DirectorySpiderMaxHops int `env:"ORLY_DIRECTORY_SPIDER_HOPS" default:"3" usage:"maximum hops for relay discovery from seed users"`
|
||||
|
||||
PolicyEnabled bool `env:"ORLY_POLICY_ENABLED" default:"false" usage:"enable policy-based event processing (configuration found in $HOME/.config/ORLY/policy.json)"`
|
||||
PolicyEnabled bool `env:"ORLY_POLICY_ENABLED" default:"false" usage:"enable policy-based event processing (default config: $HOME/.config/ORLY/policy.json)"`
|
||||
PolicyPath string `env:"ORLY_POLICY_PATH" usage:"ABSOLUTE path to policy configuration file (MUST start with /); overrides default location; relative paths are rejected"`
|
||||
|
||||
// NIP-43 Relay Access Metadata and Requests
|
||||
NIP43Enabled bool `env:"ORLY_NIP43_ENABLED" default:"false" usage:"enable NIP-43 relay access metadata and invite system"`
|
||||
|
||||
@@ -3,9 +3,7 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"lol.mleku.dev/log"
|
||||
"git.mleku.dev/mleku/nostr/encoders/event"
|
||||
"git.mleku.dev/mleku/nostr/encoders/filter"
|
||||
@@ -76,8 +74,8 @@ func (l *Listener) HandlePolicyConfigUpdate(ev *event.E) error {
|
||||
|
||||
log.I.F("policy config validation passed")
|
||||
|
||||
// Get config path for saving
|
||||
configPath := filepath.Join(xdg.ConfigHome, l.Config.AppName, "policy.json")
|
||||
// Get config path for saving (uses custom path if set, otherwise default)
|
||||
configPath := l.policyManager.ConfigPath()
|
||||
|
||||
// 3. Pause ALL message processing (lock mutex)
|
||||
// Note: We need to release the RLock first (which caller holds), then acquire exclusive Lock
|
||||
|
||||
@@ -74,7 +74,7 @@ func setupPolicyTestListener(t *testing.T, policyAdminHex string) (*Listener, *d
|
||||
}
|
||||
|
||||
// Create policy manager - now config file exists at XDG path
|
||||
policyManager := policy.NewWithManager(ctx, cfg.AppName, cfg.PolicyEnabled)
|
||||
policyManager := policy.NewWithManager(ctx, cfg.AppName, cfg.PolicyEnabled, "")
|
||||
|
||||
server := &Server{
|
||||
Ctx: ctx,
|
||||
|
||||
@@ -87,7 +87,7 @@ func Run(
|
||||
l.sprocketManager = NewSprocketManager(ctx, cfg.AppName, cfg.SprocketEnabled)
|
||||
|
||||
// Initialize policy manager
|
||||
l.policyManager = policy.NewWithManager(ctx, cfg.AppName, cfg.PolicyEnabled)
|
||||
l.policyManager = policy.NewWithManager(ctx, cfg.AppName, cfg.PolicyEnabled, cfg.PolicyPath)
|
||||
|
||||
// Merge policy-defined owners with environment-defined owners
|
||||
// This allows cloud deployments to add owners via policy.json when env vars cannot be modified
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
||||
git.mleku.dev/mleku/nostr v1.0.8 h1:YYREdIxobEqYkzxQ7/5ALACPzLkiHW+CTira+VvSQZk=
|
||||
git.mleku.dev/mleku/nostr v1.0.8/go.mod h1:iYTlg2WKJXJ0kcsM6QBGOJ0UDiJidMgL/i64cHyPjZc=
|
||||
git.mleku.dev/mleku/nostr v1.0.9 h1:aiN0ihnXzEpboXjW4u8qr5XokLQqg4P0XSZ1Y273qM0=
|
||||
git.mleku.dev/mleku/nostr v1.0.9/go.mod h1:iYTlg2WKJXJ0kcsM6QBGOJ0UDiJidMgL/i64cHyPjZc=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg=
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestBugReproduction_WithPolicyManager(t *testing.T) {
|
||||
|
||||
// Create policy with manager (enabled)
|
||||
ctx := context.Background()
|
||||
policy := NewWithManager(ctx, "ORLY", true)
|
||||
policy := NewWithManager(ctx, "ORLY", true, "")
|
||||
|
||||
// Load policy from file
|
||||
if err := policy.LoadFromFile(policyPath); err != nil {
|
||||
|
||||
@@ -31,7 +31,7 @@ func setupTestPolicy(t *testing.T, appName string) (*P, func()) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
||||
policy := NewWithManager(ctx, appName, true)
|
||||
policy := NewWithManager(ctx, appName, true, "")
|
||||
if policy == nil {
|
||||
cancel()
|
||||
os.RemoveAll(configDir)
|
||||
|
||||
@@ -29,7 +29,7 @@ func setupHotreloadTestPolicy(t *testing.T, appName string) (*P, func()) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
||||
policy := NewWithManager(ctx, appName, true)
|
||||
policy := NewWithManager(ctx, appName, true, "")
|
||||
if policy == nil {
|
||||
cancel()
|
||||
os.RemoveAll(configDir)
|
||||
|
||||
@@ -514,12 +514,19 @@ type PolicyManager struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
configDir string
|
||||
configPath string // Path to policy.json file
|
||||
scriptPath string // Default script path for backward compatibility
|
||||
enabled bool
|
||||
mutex sync.RWMutex
|
||||
runners map[string]*ScriptRunner // Map of script path -> runner
|
||||
}
|
||||
|
||||
// ConfigPath returns the path to the policy configuration file.
|
||||
// This is used by hot-reload handlers to know where to save updated policy.
|
||||
func (pm *PolicyManager) ConfigPath() string {
|
||||
return pm.configPath
|
||||
}
|
||||
|
||||
// P represents a complete policy configuration for a Nostr relay.
|
||||
// It defines access control rules, kind filtering, and default behavior.
|
||||
// Policies are evaluated in order: global rules, kind filtering, specific rules, then default policy.
|
||||
@@ -695,6 +702,15 @@ func (p *P) IsEnabled() bool {
|
||||
return p != nil && p.manager != nil && p.manager.IsEnabled()
|
||||
}
|
||||
|
||||
// ConfigPath returns the path to the policy configuration file.
|
||||
// Delegates to the internal PolicyManager.
|
||||
func (p *P) ConfigPath() string {
|
||||
if p == nil || p.manager == nil {
|
||||
return ""
|
||||
}
|
||||
return p.manager.ConfigPath()
|
||||
}
|
||||
|
||||
// getDefaultPolicyAction returns true if the default policy is "allow", false if "deny"
|
||||
func (p *P) getDefaultPolicyAction() (allowed bool) {
|
||||
switch p.DefaultPolicy {
|
||||
@@ -711,10 +727,29 @@ func (p *P) getDefaultPolicyAction() (allowed bool) {
|
||||
// NewWithManager creates a new policy with a policy manager for script execution.
|
||||
// It initializes the policy manager, loads configuration from files, and starts
|
||||
// background processes for script management and periodic health checks.
|
||||
func NewWithManager(ctx context.Context, appName string, enabled bool) *P {
|
||||
//
|
||||
// The customPolicyPath parameter allows overriding the default policy file location.
|
||||
// If empty, uses the default path: $HOME/.config/{appName}/policy.json
|
||||
// If provided, it MUST be an absolute path (starting with /) or the function will panic.
|
||||
func NewWithManager(ctx context.Context, appName string, enabled bool, customPolicyPath string) *P {
|
||||
configDir := filepath.Join(xdg.ConfigHome, appName)
|
||||
scriptPath := filepath.Join(configDir, "policy.sh")
|
||||
configPath := filepath.Join(configDir, "policy.json")
|
||||
|
||||
// Determine the policy config path
|
||||
var configPath string
|
||||
if customPolicyPath != "" {
|
||||
// Validate that custom path is absolute
|
||||
if !filepath.IsAbs(customPolicyPath) {
|
||||
panic(fmt.Sprintf("FATAL: ORLY_POLICY_PATH must be an ABSOLUTE path (starting with /), got: %q", customPolicyPath))
|
||||
}
|
||||
configPath = customPolicyPath
|
||||
// Update configDir to match the custom path's directory for script resolution
|
||||
configDir = filepath.Dir(customPolicyPath)
|
||||
scriptPath = filepath.Join(configDir, "policy.sh")
|
||||
log.I.F("using custom policy path: %s", configPath)
|
||||
} else {
|
||||
configPath = filepath.Join(configDir, "policy.json")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
@@ -722,6 +757,7 @@ func NewWithManager(ctx context.Context, appName string, enabled bool) *P {
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
configDir: configDir,
|
||||
configPath: configPath,
|
||||
scriptPath: scriptPath,
|
||||
enabled: enabled,
|
||||
runners: make(map[string]*ScriptRunner),
|
||||
|
||||
@@ -825,7 +825,7 @@ func TestNewWithManager(t *testing.T) {
|
||||
// Test with disabled policy (doesn't require policy.json file)
|
||||
t.Run("disabled policy", func(t *testing.T) {
|
||||
enabled := false
|
||||
policy := NewWithManager(ctx, appName, enabled)
|
||||
policy := NewWithManager(ctx, appName, enabled, "")
|
||||
|
||||
if policy == nil {
|
||||
t.Fatal("Expected policy but got nil")
|
||||
|
||||
@@ -31,7 +31,7 @@ func setupTagValidationTestPolicy(t *testing.T, appName string) (*P, func()) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
||||
policy := NewWithManager(ctx, appName, true)
|
||||
policy := NewWithManager(ctx, appName, true, "")
|
||||
if policy == nil {
|
||||
cancel()
|
||||
os.RemoveAll(configDir)
|
||||
|
||||
@@ -1 +1 @@
|
||||
v0.35.2
|
||||
v0.35.3
|
||||
Reference in New Issue
Block a user