Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
9473d09
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
c608efc
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
046ac3b
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
7b4cbbe
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
a0551ff
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
a36312e
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
d70e2c4
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
489a28a
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
53a7035
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
3b078d0
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
4b4d249
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
70ed186
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
e03dfcc
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
b538176
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
f021fd6
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
3b54d3f
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
e1dd56d
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
800c4f9
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
f13e641
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
fbcf12d
feat(quickstart): add runnable quickstart, recipes, and fixtures (iss…
levleontiev Mar 17, 2026
a93b377
docs(readme): add quickstart pointer, update project layout, fix benc…
levleontiev Mar 17, 2026
a4ad21c
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
411c6c7
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
288c9c7
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
80365c9
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
e778599
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
399cd93
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
e327a0c
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
ee2ab17
fix(quickstart): fix review issues — reverse_proxy mode, correct conf…
levleontiev Mar 18, 2026
348789e
refactor: replace provider-failover recipe with circuit-breaker
Mar 18, 2026
58fa21e
Merge branch 'main' into feature/issue-32-quickstart
levleontiev Mar 19, 2026
7a13c7f
docs: add wrapper mode to README + integration links in comparison se…
levleontiev Mar 19, 2026
480a409
docs: rewrite LLM token budget section to showcase wrapper mode
levleontiev Mar 19, 2026
c297873
docs: wrapper mode selector pathPrefix "/" covers all providers
levleontiev Mar 19, 2026
989cc04
docs: replace ASCII architecture diagrams with Mermaid sequence diagrams
levleontiev Mar 19, 2026
a2bb9da
docs: fix JWT wording — Fairvisor parses claims, does not validate si…
levleontiev Mar 19, 2026
4759b72
trim README: remove benchmark methodology, Contributing section; fix …
levleontiev Mar 19, 2026
5fac6e2
README: rework tagline, add hook + mode selector, drop Policy as code…
levleontiev Mar 22, 2026
b4e2b4d
README: remove hero latency/RPS line
levleontiev Mar 22, 2026
c75e2a5
README: broaden hook paragraph — not LLM-only
levleontiev Mar 22, 2026
51d45ae
docs: add 'Why we built this' section and ToC entry
levleontiev Mar 22, 2026
0a9d1ed
docs: README final polish — tagline, quickstart restructure, badge fi…
levleontiev Mar 22, 2026
c7d1345
fix(recipes): replace unsupported loop_detector rule with spec-level …
levleontiev Mar 22, 2026
a7f935d
fix(recipes): change cost_based period "30d" → "7d" (only 5m/1h/1d/7d…
levleontiev Mar 22, 2026
8566c15
docs: rewrite 'no external state' bullet to focus on request latency
levleontiev Mar 22, 2026
1e9ffc6
docs: reword 'does not replace' — Fairvisor can run standalone or alo…
levleontiev Mar 22, 2026
96de2a1
docs(readme): fix quickstart clone path
codex Mar 22, 2026
a4b1663
fix(quickstart): build local image instead of ghcr
codex Mar 22, 2026
4a6a52c
docs(quickstart): rename compose service to fairvisor
codex Mar 22, 2026
a739b5b
chore(quickstart): reduce readyz healthcheck frequency
codex Mar 22, 2026
3ba11ce
chore(quickstart): remove mock_llm healthcheck
codex Mar 22, 2026
78dc693
chore(quickstart): restore mock_llm healthcheck at low frequency
codex Mar 22, 2026
cfd558e
fix(docker): add maxminddb dev lib and tune map hashes
codex Mar 22, 2026
111d14d
fix(nginx): increase map hash size for quickstart
codex Mar 22, 2026
a033204
fix(nginx): set map_hash_max_size 131072 to cover all 85k ASN entries
levleontiev Mar 22, 2026
7772219
fix(nginx): map_hash_max_size 262144 — need ~3x entries for collision…
levleontiev Mar 22, 2026
8a73777
fix(cli): add bin/fairvisor-cli with corrected -I path for cli.* modules
levleontiev Mar 22, 2026
6092703
fix(cli): Dockerfile.cli → bin/fairvisor-cli, ENTRYPOINT updated
levleontiev Mar 22, 2026
fac3632
docs(cli): rename fairvisor → fairvisor-cli, fix resty -I path in docs
levleontiev Mar 22, 2026
f2059fc
docs(readme): fairvisor → fairvisor-cli in CLI section
levleontiev Mar 22, 2026
72150be
Merge branch 'main' into feature/issue-32-quickstart
levleontiev Mar 23, 2026
ade005a
feat(limiter): improve token count accuracy and max_completion_tokens…
codex Mar 23, 2026
96fbdc1
fix(limiter): fix luacheck warnings — trailing whitespace and unused …
codex Mar 23, 2026
843a26c
test(limiter): add scenarios for max_completion_tokens and improved J…
codex Mar 23, 2026
64b3b58
test(limiter): cover max_tokens body field and default fallback paths
codex Mar 23, 2026
2749a67
Merge branch 'main' into feature/issue-32-quickstart
levleontiev Mar 25, 2026
fa72ccd
docs: update README subtitle to sharper tagline
codex Mar 26, 2026
524b172
Merge main into feature/issue-32-quickstart
codex Mar 26, 2026
523f65e
chore: unify CLI naming to fairvisor-cli and cleanup root
codex Mar 26, 2026
c08e04a
chore: rename CLI binary from fairvisor-cli to fairvisor
codex Mar 27, 2026
2ad77c5
chore: finish fairvisor-cli → fairvisor rename in cli/ and bin/ci/
codex Mar 27, 2026
5ddb3ad
docs: remove fairvisor connect from CLI section (SaaS not yet available)
codex Mar 28, 2026
230f0ad
fix(cli): handle nil read_err in validate command when file read retu…
codex Mar 29, 2026
8fe2981
fix(cli): fix nil read_err crash in test command; add lua-resty-http …
codex Mar 29, 2026
d5c50d6
fix(cli): remove version pin from lua-resty-http opm install (wrong v…
codex Mar 29, 2026
75a7f56
fix(supply-chain): pin lua-resty-http=0.17.1 and fix OPM version checker
codex Mar 29, 2026
f431939
fix(cli): move mock_ngx to cli/lib so fairvisor test works in Docker
codex Mar 29, 2026
5882652
fix(cli): status command reads policy_version from /readyz; show 0 de…
codex Mar 29, 2026
138125d
fix(lint): exclude cli/lib/mock_ngx.lua from luacheck
codex Mar 29, 2026
a0d14b0
fix(rule_engine): log state transitions at INFO instead of WARN
levleontiev Mar 29, 2026
073d5d7
docs: fix decision service allow status in README
codex Mar 29, 2026
40f3548
Merge branch 'main' into feature/issue-32-quickstart
levleontiev Mar 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ globals = {
max_line_length = 140

-- Exclude generated or vendored paths
exclude_files = { "spec/helpers/mock_ngx.lua" }
exclude_files = { "spec/helpers/mock_ngx.lua", "cli/lib/mock_ngx.lua" }

-- Test files monkey-patch standard globals (math.random, os.getenv)
-- and use intentionally-unused callback arguments (self, ctx).
Expand Down
288 changes: 166 additions & 122 deletions README.md

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions bin/ci/check_unpinned_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
SHA_RE = re.compile(r"^[0-9a-f]{40}$")
USES_RE = re.compile(r"uses:\s*([^\s#]+)")
FROM_RE = re.compile(r"^\s*FROM\s+([^\s]+)", re.MULTILINE)
OPM_RE = re.compile(r"^\s*RUN\s+opm\s+get\s+([^\s\\]+)(?:\s+([^\s\\#]+))?", re.MULTILINE)
OPM_RE = re.compile(r"^\s*RUN\s+opm\s+get\s+([^\s\\]+)(?:[^\S\n]+([^\s\\#]+))?", re.MULTILINE)


@dataclass(frozen=True)
Expand Down Expand Up @@ -79,7 +79,9 @@ def scan_dockerfile(path: Path) -> list[Finding]:
for match in OPM_RE.finditer(text):
package = match.group(1)
version = match.group(2)
if not version or version.startswith(("&", "|")):
# OPM uses "pkg=version" (embedded) or "pkg version" (space-separated)
is_pinned = "=" in package or (version and not version.startswith(("&", "|")))
if not is_pinned:
findings.append(
Finding(
category="opm-package",
Expand Down
2 changes: 1 addition & 1 deletion bin/fairvisor
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

exec resty -I "${SCRIPT_DIR}/src" -I "${SCRIPT_DIR}/cli" \
exec resty -I "${SCRIPT_DIR}/src" -I "${SCRIPT_DIR}" \
"${SCRIPT_DIR}/cli/main.lua" "$@"
4 changes: 2 additions & 2 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ From the repo root:
Or with `resty` directly (e.g. from another directory, adjusting `-I` paths):

```bash
resty -I /path/to/fv-oss/src -I /path/to/fv-oss/cli /path/to/fv-oss/cli/main.lua <command> [options]
resty -I /path/to/fv-oss/src -I /path/to/fv-oss /path/to/fv-oss/cli/main.lua <command> [options]
```

`bin/fairvisor` sets `-I` to the repo's `src` and `cli` so that `require("cli.commands.init")` and `require("fairvisor.bundle_loader")` resolve correctly.
`bin/fairvisor` sets `-I` to the repo's `src` and root (for `cli.*` modules) so that `require("cli.commands.init")` and `require("fairvisor.bundle_loader")` resolve correctly.

## Commands

Expand Down
18 changes: 16 additions & 2 deletions cli/commands/status.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,28 @@ function _M.run(argv)
return nil, 2
end

local policy_version = "unknown"
if health_res.body and health_res.body ~= "" then
local ok_json, cjson = pcall(require, "cjson")
if ok_json then
local ok_dec, decoded = pcall(cjson.decode, health_res.body)
if ok_dec and type(decoded) == "table" and decoded.policy_version ~= nil then
policy_version = tostring(decoded.policy_version)
end
end
end

local metrics_res = httpc:request_uri(edge_url .. "/metrics")
local metrics_body = metrics_res and metrics_res.body or ""

local decisions_raw = _parse_metric(metrics_body, "fairvisor_decisions_total")
local decisions = (decisions_raw ~= "unknown") and decisions_raw or "0"

local data = {
status = (health_res.status == 200 and "ready") or "not ready",
policy_version = _parse_metric(metrics_body, "fairvisor_bundle_version"),
policy_version = policy_version,
saas = (_parse_metric(metrics_body, "fairvisor_saas_reachable") == "1") and "connected" or "disconnected",
decisions = _parse_metric(metrics_body, "fairvisor_decisions_total"),
decisions = decisions,
}

if format == "json" then
Expand Down
13 changes: 8 additions & 5 deletions cli/commands/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ local function _read_file(path)

local content = handle:read("*a")
handle:close()
if content == nil then
return nil, "failed to read file content"
end
return content
end

Expand Down Expand Up @@ -47,7 +50,7 @@ local function _generate_mock_requests(bundle)
headers = {},
query_params = {},
ip_address = "127.0.0.1",
user_agent = "fairvisor-cli/test",
user_agent = "fairvisor/test",
}
end

Expand All @@ -58,7 +61,7 @@ local function _generate_mock_requests(bundle)
headers = {},
query_params = {},
ip_address = "127.0.0.1",
user_agent = "fairvisor-cli/test",
user_agent = "fairvisor/test",
}
end

Expand Down Expand Up @@ -127,7 +130,7 @@ function _M.run(argv)

local content, read_err = _read_file(file)
if not content then
output.print_error("Cannot read file: " .. read_err)
output.print_error("Cannot read file: " .. (read_err or "unknown read error"))
return nil, 1
end

Expand All @@ -142,7 +145,7 @@ function _M.run(argv)
return nil, 1
end

local ok_mock, mock_ngx = pcall(require, "spec.helpers.mock_ngx")
local ok_mock, mock_ngx = pcall(require, "cli.lib.mock_ngx")
if not ok_mock then
output.print_error("mock_ngx helper is unavailable: " .. mock_ngx)
return nil, 1
Expand All @@ -168,7 +171,7 @@ function _M.run(argv)
if requests_file then
requests, read_err = _load_requests(requests_file)
if not requests then
output.print_error("Cannot load requests: " .. read_err)
output.print_error("Cannot load requests: " .. (read_err or "unknown read error"))
return nil, 1
end
else
Expand Down
5 changes: 4 additions & 1 deletion cli/commands/validate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ local function _read_file(path)

local content = handle:read("*a")
handle:close()
if content == nil then
return nil, "failed to read file content"
end
return content
end

Expand Down Expand Up @@ -101,7 +104,7 @@ function _M.run(argv)

local content, read_err = _read_file(file)
if not content then
output.print_error("Cannot read file: " .. read_err)
output.print_error("Cannot read file: " .. (read_err or "unknown read error"))
return nil, 1
end

Expand Down
Loading
Loading