Skip to content

Commit 40d83f8

Browse files
committed
docs(user): improve guides and add YAML support for policy config
1 parent dce0d35 commit 40d83f8

File tree

8 files changed

+100
-97
lines changed

8 files changed

+100
-97
lines changed

NEXT_STEPS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
## Immediate Actions
99
- [x] **Review Documentation**: Verify the newly created user docs in `docs/` align with the implementation and are clear.
1010
- [x] **M42.4 (Audit)**: Updated `docs/index.md` to link all guides (`web-ui.md`, `cli.md`) and verified `docs/installation.md` and `docs/configuration.md`.
11+
- [x] **M42.5 (Improvement)**: Enhanced `docs/installation.md`, switched to YAML in `docs/configuration.md` & `docs/guides/deployment.md`, and implemented YAML support in `pkg/engine`.
1112
- [x] **M42.3 (Refined)**: Added Policy Guide and moved/refined Deployment Guide.
1213
- [x] **SDK Docs**: Refreshed JS, Python, and Go SDK documentation to remove draft warnings and match implementation.
1314
- [ ] **Phase 16 Continues**: Run advanced simulation scenarios (Epic 28) or focus on release prep.

PROGRESS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,4 @@
8484
| M42.3: User Guides (CLI, Web UI, API Updates) | COMPLETED | Docs | 2026-02-07 |
8585
| M42.3+: Guides Refinement (Policy & Deployment) | COMPLETED | Docs | 2026-02-07 |
8686
| M42.3++: SDK Documentation Refresh (JS/Py/Go) | COMPLETED | Docs | 2026-02-07 |
87-
| M42.4: User Docs Index & Link Audit | COMPLETED | Orchestrator | 2026-02-07 |
87+
| M42.5: User Docs Improvement (YAML Support & Verification) | COMPLETED | Orchestrator | 2026-02-07 |

docs/configuration.md

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,27 @@ The configuration file has four main sections:
3636
3. **`pricing`**: (Optional) Defines cost models for financial governance.
3737
4. **`retention`**: (Optional) Defines data lifecycle rules for events.
3838

39-
### Example: `policy.json`
39+
### Example: `policy.yaml`
4040

4141
This example tracks GitHub API limits and enforces a hard stop when the limit is exceeded.
4242

43-
```json
44-
{
45-
"policies": [
46-
{
47-
"id": "github-core-hard-limit",
48-
"scope": "global",
49-
"type": "hard",
50-
"limit": 5000,
51-
"rules": [
52-
{
53-
"name": "deny-exceeded-limit",
54-
"condition": "remaining < 0",
55-
"action": "deny",
56-
"params": {
57-
"reason": "hard limit exceeded for GitHub core requests"
58-
}
59-
}
60-
]
61-
}
62-
],
63-
"providers": {
64-
"github": [
65-
{
66-
"id": "github-main",
67-
"token_env_var": "GITHUB_TOKEN"
68-
}
69-
]
70-
}
71-
}
43+
```yaml
44+
policies:
45+
- id: "github-core-hard-limit"
46+
scope: "global"
47+
type: "hard"
48+
limit: 5000
49+
rules:
50+
- name: "deny-exceeded-limit"
51+
condition: "remaining < 0"
52+
action: "deny"
53+
params:
54+
reason: "hard limit exceeded for GitHub core requests"
55+
56+
providers:
57+
github:
58+
- id: "github-main"
59+
token_env_var: "GITHUB_TOKEN"
7260
```
7361
7462
### Policy Rules
@@ -94,16 +82,12 @@ The `providers` section configures the "Ingestion Layer". It tells Ratelord how
9482

9583
Tracks `core`, `search`, and `graphql` rate limits.
9684

97-
```json
98-
"providers": {
99-
"github": [
100-
{
101-
"id": "my-org-github",
102-
"token_env_var": "GITHUB_TOKEN",
103-
"enterprise_url": "https://github.example.com/api/v3"
104-
}
105-
]
106-
}
85+
```yaml
86+
providers:
87+
github:
88+
- id: "my-org-github"
89+
token_env_var: "GITHUB_TOKEN"
90+
enterprise_url: "https://github.example.com/api/v3"
10791
```
10892

10993
- `token_env_var`: The environment variable containing the Personal Access Token (PAT).
@@ -113,16 +97,12 @@ Tracks `core`, `search`, and `graphql` rate limits.
11397

11498
Tracks Request-Per-Minute (RPM) and Token-Per-Minute (TPM) limits.
11599

116-
```json
117-
"providers": {
118-
"openai": [
119-
{
120-
"id": "openai-prod",
121-
"api_key_env_var": "OPENAI_API_KEY",
122-
"org_id": "org-12345"
123-
}
124-
]
125-
}
100+
```yaml
101+
providers:
102+
openai:
103+
- id: "openai-prod"
104+
api_key_env_var: "OPENAI_API_KEY"
105+
org_id: "org-12345"
126106
```
127107

128108
- `api_key_env_var`: The environment variable containing the API Key.

docs/guides/deployment.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This guide covers deployment via Systemd, Docker, and Kubernetes.
99
- **Binary**: `ratelord-d` is a standalone Go binary.
1010
- **State**: Persisted to `ratelord.db` (SQLite WAL mode). **Requires a persistent filesystem.**
1111
- **Configuration**:
12-
- `policy.json` / `policy.yaml`: Defines pools, windows, and burst limits.
12+
- `policy.yaml` / `policy.yaml`: Defines pools, windows, and burst limits.
1313
- Environment Variables: Store sensitive tokens (e.g., `GITHUB_TOKEN`, `OPENAI_API_KEY`).
1414
- **Network**: Exposes an HTTP API on port `8090`.
1515
- **Signals**:
@@ -41,7 +41,7 @@ sudo chown ratelord:ratelord /var/lib/ratelord
4141

4242
# Place config
4343
sudo mkdir -p /etc/ratelord
44-
sudo cp policy.json /etc/ratelord/
44+
sudo cp policy.yaml /etc/ratelord/
4545
```
4646

4747
### 3.2. Unit File (`/etc/systemd/system/ratelord.service`)
@@ -57,7 +57,7 @@ Type=simple
5757
User=ratelord
5858
Group=ratelord
5959
# Adjust path to binary
60-
ExecStart=/usr/local/bin/ratelord-d --config /etc/ratelord/policy.json --db /var/lib/ratelord/ratelord.db --addr 127.0.0.1:8090
60+
ExecStart=/usr/local/bin/ratelord-d --config /etc/ratelord/policy.yaml --db /var/lib/ratelord/ratelord.db --addr 127.0.0.1:8090
6161
ExecReload=/bin/kill -HUP $MAINPID
6262
KillMode=process
6363
Restart=on-failure
@@ -102,12 +102,12 @@ WORKDIR /app
102102
# Install ca-certificates if ratelord needs to make outbound TLS calls (e.g. to upstream APIs)
103103
RUN apk add --no-cache ca-certificates
104104
COPY --from=builder /app/ratelord-d /usr/local/bin/
105-
COPY policy.json /etc/ratelord/policy.json
105+
COPY policy.yaml /etc/ratelord/policy.yaml
106106

107107
# State volume
108108
VOLUME /data
109109
ENV RATELORD_DB_PATH=/data/ratelord.db
110-
ENV RATELORD_CONFIG_PATH=/etc/ratelord/policy.json
110+
ENV RATELORD_CONFIG_PATH=/etc/ratelord/policy.yaml
111111
ENV RATELORD_ADDR=0.0.0.0:8090
112112

113113
CMD ["ratelord-d"]
@@ -124,7 +124,7 @@ services:
124124
restart: always
125125
volumes:
126126
- ratelord_data:/data
127-
- ./policy.json:/etc/ratelord/policy.json:ro
127+
- ./policy.yaml:/etc/ratelord/policy.yaml:ro
128128
environment:
129129
- GITHUB_TOKEN=${GITHUB_TOKEN}
130130
- OPENAI_API_KEY=${OPENAI_API_KEY}
@@ -168,7 +168,7 @@ spec:
168168
args:
169169
- "--addr=127.0.0.1:8090"
170170
- "--db=/data/ratelord.db"
171-
- "--config=/etc/config/policy.json"
171+
- "--config=/etc/config/policy.yaml"
172172
volumeMounts:
173173
- name: ratelord-data
174174
mountPath: /data
@@ -194,20 +194,20 @@ spec:
194194
If you run multiple replicas of `ratelord` (e.g., 3 sidecars for 3 app replicas), **state is isolated per pod**.
195195
- **Pros**: Zero coordination latency.
196196
- **Cons**: Global limits (e.g., "1000 req/min across all pods") effectively become `N * Limit`.
197-
- **Mitigation**: Use `policy.json` to define *per-instance* limits, or divide your global quota by the expected replica count ($Limit_{local} = Limit_{global} / N$).
197+
- **Mitigation**: Use `policy.yaml` to define *per-instance* limits, or divide your global quota by the expected replica count ($Limit_{local} = Limit_{global} / N$).
198198

199199
---
200200

201201
## 6. Configuration & Secrets Management
202202

203-
### Policy (`policy.json`)
203+
### Policy (`policy.yaml`)
204204
- Treat as code. Version control it.
205205
- **Updates**:
206206
- **K8s**: Update ConfigMap -> Wait for volume update -> Send `SIGHUP` to sidecar (or let K8s restart it).
207207
- **Systemd**: Update file -> `systemctl reload ratelord`.
208208

209209
### Secrets
210-
- **NEVER** put tokens in `policy.json`.
210+
- **NEVER** put tokens in `policy.yaml`.
211211
- `ratelord-d` reads tokens from environment variables referenced in the policy (if feature supported) or injects them directly if acting as a proxy.
212212
- Use Kubernetes Secrets or `.env` files.
213213

docs/installation.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,18 @@ go install github.com/rmax-ai/ratelord/cmd/ratelord-d@latest
1414
go install github.com/rmax-ai/ratelord/cmd/ratelord-tui@latest
1515
```
1616

17+
### Verification
18+
19+
Verify the installation by checking the versions:
20+
21+
```bash
22+
ratelord version
23+
ratelord-d --version
24+
```
25+
1726
### From Releases
1827

19-
Download pre-compiled binaries from the [GitHub Releases](https://github.com/rmax-ai/ratelord/releases) page.
28+
Download pre-compiled binaries for your platform (macOS, Linux, Windows) from the [GitHub Releases](https://github.com/rmax-ai/ratelord/releases) page.
2029

2130
## 2. Quickstart (Local)
2231

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/prometheus/client_golang v1.23.2
1616
github.com/redis/go-redis/v9 v9.17.3
1717
github.com/stretchr/testify v1.11.1
18+
gopkg.in/yaml.v3 v3.0.1
1819
)
1920

2021
require (
@@ -55,5 +56,4 @@ require (
5556
golang.org/x/sys v0.36.0 // indirect
5657
golang.org/x/text v0.28.0 // indirect
5758
google.golang.org/protobuf v1.36.8 // indirect
58-
gopkg.in/yaml.v3 v3.0.1 // indirect
5959
)

pkg/engine/config.go

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ package engine
22

33
// PolicyConfig represents the top-level structure of policy.json
44
type PolicyConfig struct {
5-
Policies []PolicyDefinition `json:"policies"`
6-
Providers ProvidersConfig `json:"providers,omitempty"`
7-
Pricing map[string]map[string]int64 `json:"pricing,omitempty"`
8-
Retention *RetentionConfig `json:"retention,omitempty"`
5+
Policies []PolicyDefinition `json:"policies" yaml:"policies"`
6+
Providers ProvidersConfig `json:"providers,omitempty" yaml:"providers,omitempty"`
7+
Pricing map[string]map[string]int64 `json:"pricing,omitempty" yaml:"pricing,omitempty"`
8+
Retention *RetentionConfig `json:"retention,omitempty" yaml:"retention,omitempty"`
99
}
1010

1111
// RetentionConfig defines data lifecycle rules
1212
type RetentionConfig struct {
13-
Enabled bool `json:"enabled"`
14-
DefaultTTL string `json:"default_ttl"` // e.g., "720h"
15-
ByType map[string]string `json:"by_type,omitempty"` // event_type -> "24h"
16-
CheckInterval string `json:"check_interval,omitempty"` // e.g., "1h"
13+
Enabled bool `json:"enabled" yaml:"enabled"`
14+
DefaultTTL string `json:"default_ttl" yaml:"default_ttl"` // e.g., "720h"
15+
ByType map[string]string `json:"by_type,omitempty" yaml:"by_type,omitempty"` // event_type -> "24h"
16+
CheckInterval string `json:"check_interval,omitempty" yaml:"check_interval,omitempty"` // e.g., "1h"
1717
}
1818

1919
// GetCost looks up the cost per unit for a given provider and pool.
@@ -32,47 +32,47 @@ func (c *PolicyConfig) GetCost(providerID, poolID string) int64 {
3232

3333
// ProvidersConfig holds configuration for various providers
3434
type ProvidersConfig struct {
35-
GitHub []GitHubConfig `json:"github,omitempty"`
36-
OpenAI []OpenAIConfig `json:"openai,omitempty"`
35+
GitHub []GitHubConfig `json:"github,omitempty" yaml:"github,omitempty"`
36+
OpenAI []OpenAIConfig `json:"openai,omitempty" yaml:"openai,omitempty"`
3737
}
3838

3939
// GitHubConfig defines configuration for the GitHub provider
4040
type GitHubConfig struct {
41-
ID string `json:"id"`
42-
TokenEnvVar string `json:"token_env_var"` // Prefer env var name for security
43-
EnterpriseURL string `json:"enterprise_url,omitempty"`
41+
ID string `json:"id" yaml:"id"`
42+
TokenEnvVar string `json:"token_env_var" yaml:"token_env_var"` // Prefer env var name for security
43+
EnterpriseURL string `json:"enterprise_url,omitempty" yaml:"enterprise_url,omitempty"`
4444
}
4545

4646
// OpenAIConfig defines configuration for the OpenAI provider
4747
type OpenAIConfig struct {
48-
ID string `json:"id"`
49-
TokenEnvVar string `json:"token_env_var"`
50-
OrgID string `json:"org_id,omitempty"` // Optional: for 'OpenAI-Organization' header
51-
BaseURL string `json:"base_url,omitempty"`
48+
ID string `json:"id" yaml:"id"`
49+
TokenEnvVar string `json:"token_env_var" yaml:"token_env_var"`
50+
OrgID string `json:"org_id,omitempty" yaml:"org_id,omitempty"` // Optional: for 'OpenAI-Organization' header
51+
BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"`
5252
}
5353

5454
// PolicyDefinition maps a high-level policy block
5555
type PolicyDefinition struct {
56-
ID string `json:"id"`
57-
Scope string `json:"scope"` // e.g., "global", "env:dev"
58-
Type string `json:"type"` // "hard", "soft"
59-
Limit int64 `json:"limit,omitempty"`
60-
Rules []RuleDefinition `json:"rules"`
56+
ID string `json:"id" yaml:"id"`
57+
Scope string `json:"scope" yaml:"scope"` // e.g., "global", "env:dev"
58+
Type string `json:"type" yaml:"type"` // "hard", "soft"
59+
Limit int64 `json:"limit,omitempty" yaml:"limit,omitempty"`
60+
Rules []RuleDefinition `json:"rules" yaml:"rules"`
6161
}
6262

6363
// RuleDefinition maps individual logic rules
6464
type RuleDefinition struct {
65-
Name string `json:"name"`
66-
Condition string `json:"condition"` // Simple DSL: "remaining < 100"
67-
Action string `json:"action"` // "approve", "deny", "shape"
68-
Params map[string]interface{} `json:"params,omitempty"`
69-
TimeWindow *TimeWindow `json:"time_window,omitempty"`
65+
Name string `json:"name" yaml:"name"`
66+
Condition string `json:"condition" yaml:"condition"` // Simple DSL: "remaining < 100"
67+
Action string `json:"action" yaml:"action"` // "approve", "deny", "shape"
68+
Params map[string]interface{} `json:"params,omitempty" yaml:"params,omitempty"`
69+
TimeWindow *TimeWindow `json:"time_window,omitempty" yaml:"time_window,omitempty"`
7070
}
7171

7272
// TimeWindow defines a temporal constraint for a rule
7373
type TimeWindow struct {
74-
StartTime string `json:"start_time,omitempty"` // HH:MM (24-hour)
75-
EndTime string `json:"end_time,omitempty"` // HH:MM (24-hour)
76-
Days []string `json:"days,omitempty"` // ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
77-
Location string `json:"location,omitempty"` // e.g., "America/New_York" (defaults to UTC)
74+
StartTime string `json:"start_time,omitempty" yaml:"start_time,omitempty"` // HH:MM (24-hour)
75+
EndTime string `json:"end_time,omitempty" yaml:"end_time,omitempty"` // HH:MM (24-hour)
76+
Days []string `json:"days,omitempty" yaml:"days,omitempty"` // ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
77+
Location string `json:"location,omitempty" yaml:"location,omitempty"` // e.g., "America/New_York" (defaults to UTC)
7878
}

pkg/engine/loader.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,31 @@ package engine
33
import (
44
"encoding/json"
55
"os"
6+
"path/filepath"
7+
"strings"
8+
9+
"gopkg.in/yaml.v3"
610
)
711

8-
// LoadPolicyConfig reads and parses a policy file
12+
// LoadPolicyConfig reads and parses a policy file (JSON or YAML)
913
func LoadPolicyConfig(path string) (*PolicyConfig, error) {
1014
data, err := os.ReadFile(path)
1115
if err != nil {
1216
return nil, err
1317
}
1418

1519
var config PolicyConfig
16-
if err := json.Unmarshal(data, &config); err != nil {
17-
return nil, err
20+
ext := strings.ToLower(filepath.Ext(path))
21+
22+
if ext == ".yaml" || ext == ".yml" {
23+
if err := yaml.Unmarshal(data, &config); err != nil {
24+
return nil, err
25+
}
26+
} else {
27+
// Default to JSON
28+
if err := json.Unmarshal(data, &config); err != nil {
29+
return nil, err
30+
}
1831
}
1932

2033
return &config, nil

0 commit comments

Comments
 (0)