{"id":50941611,"url":"https://github.com/aks-builds/har-to-slo","last_synced_at":"2026-06-17T15:30:53.698Z","repository":{"id":364097573,"uuid":"1266390077","full_name":"aks-builds/har-to-slo","owner":"aks-builds","description":"Turn HAR timing data into k6 SLO baselines — your recordings already know your p95","archived":false,"fork":false,"pushed_at":"2026-06-11T16:04:45.000Z","size":57,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-11T17:14:58.914Z","etag":null,"topics":["cli","devtools","grafana","grafana-k6","har","k6","load-testing","opentelemetry","p95","performance-baseline","performance-testing","slo","slo-baseline","thresholds"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/har-to-slo","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aks-builds.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T15:15:42.000Z","updated_at":"2026-06-11T16:18:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/aks-builds/har-to-slo","commit_stats":null,"previous_names":["aks-builds/har-to-slo"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/aks-builds/har-to-slo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aks-builds%2Fhar-to-slo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aks-builds%2Fhar-to-slo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aks-builds%2Fhar-to-slo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aks-builds%2Fhar-to-slo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aks-builds","download_url":"https://codeload.github.com/aks-builds/har-to-slo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aks-builds%2Fhar-to-slo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34453440,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","devtools","grafana","grafana-k6","har","k6","load-testing","opentelemetry","p95","performance-baseline","performance-testing","slo","slo-baseline","thresholds"],"created_at":"2026-06-17T15:30:52.951Z","updated_at":"2026-06-17T15:30:53.693Z","avatar_url":"https://github.com/aks-builds.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 📐 har-to-slo\n\n**Your HAR file already knows your p95. You just haven't asked it yet.**\n\nEvery time you record a HAR — from Playwright, Chrome DevTools, an API gateway export, or a browser session —\nyou're capturing real latency measurements for every route in your app. `har-to-slo` reads those measurements,\ncomputes p95 baselines per route, and outputs a ready-to-use k6 `thresholds {}` block.\n**Stop inventing SLO numbers. Derive them.**\n\n\u003e **Not a HAR-to-k6-script converter.** [`grafana/har-to-k6`](https://github.com/grafana/har-to-k6) replays traffic.\n\u003e `har-to-slo` reads the *timings* in that same file and turns them into statistically grounded SLO baselines.\n\u003e They complement each other — use both.\n\n[![CI](https://github.com/aks-builds/har-to-slo/actions/workflows/ci.yml/badge.svg)](https://github.com/aks-builds/har-to-slo/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/aks-builds/har-to-slo/actions/workflows/codeql.yml/badge.svg)](https://github.com/aks-builds/har-to-slo/actions/workflows/codeql.yml)\n[![npm version](https://img.shields.io/npm/v/har-to-slo.svg)](https://www.npmjs.com/package/har-to-slo)\n[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Agent Skill](https://img.shields.io/badge/agent-skill-8a2be2.svg)](skills/har-to-slo.md)\n\n\u003cbr/\u003e\n\n![har-to-slo running against a real 48-entry HAR — 6 routes collapsed, p95 baselines with 1.5× multiplier](.github/media/demo.svg)\n\n\u003csub\u003e☝️ Real output from a 48-entry HAR. Six routes, all HTTP methods, zero configuration.\u003c/sub\u003e\n\n\u003c/div\u003e\n\n---\n\n## The problem it solves\n\nWhen you add a k6 `thresholds {}` block, you need a number:\n\n```javascript\n\"http_req_duration{url:/api/orders}\": [\"p(95)\u003c???\"],\n```\n\nWhere does that number come from? Usually: a guess, a round number, or copied from another service.\n`har-to-slo` answers that question with **measured data from your own traffic**.\n\n---\n\n## Install\n\n```bash\n# No install needed — run with npx\nnpx har-to-slo --input recording.har\n```\n\n---\n\n## Usage\n\n```bash\n# Basic — outputs k6 thresholds block to stdout\nnpx har-to-slo --input recording.har\n\n# Tighter SLOs (1.2× p95 instead of default 1.5×)\nnpx har-to-slo --input recording.har --multiplier 1.2\n\n# JSON — for CI pipelines and tooling integrations\nnpx har-to-slo --input recording.har --format json\n\n# Write directly into your k6 script file\nnpx har-to-slo --input recording.har --output k6/thresholds.js\n\n# With LLM rationale for each threshold (requires ANTHROPIC_API_KEY)\nnpx har-to-slo --input recording.har --explain\n```\n\n---\n\n## Supported sources\n\n| Source | Command | What it reads |\n|---|---|---|\n| `har` (default) | `har-to-slo --input recording.har` | HAR file timing data |\n| `k6` | `har-to-slo --source k6 --input summary.json` | k6 `--summary-export` JSON |\n| `logs` | `har-to-slo --source logs --input nginx.log` | nginx / ALB / NDJSON access logs |\n| `otel` | `har-to-slo --source otel --input traces.jsonl` | OpenTelemetry trace JSONL export |\n| `prometheus` | `har-to-slo --source prometheus --url http://prom:9090 --query http_request_duration_seconds` | Prometheus / Mimir HTTP API |\n\n### Using with the Grafana stack\n\n```bash\n# From a k6 load test run\nk6 run --summary-export summary.json load-test.js\nhar-to-slo --source k6 --input summary.json\n\n# From Grafana Mimir (or any Prometheus)\nhar-to-slo --source prometheus \\\n  --url http://mimir.internal:9090 \\\n  --query http_request_duration_seconds \\\n  --range 30d\n\n# From Grafana Tempo trace export (OTEL JSONL)\nhar-to-slo --source otel --input tempo-export.jsonl\n```\n\n---\n\n## How it works\n\n```\nHAR file\n  └─ entry.time per request (real latency in ms)\n       └─ collapse URLs: /users/123 → /users/{id}\n            └─ group by METHOD + template\n                 └─ trim top 5% outliers\n                      └─ compute p95 per group\n                           └─ threshold = Math.round(p95 × multiplier)\n                                └─ emit k6 thresholds {} block\n```\n\nA HAR entry's `.time` field is the total wall-clock duration of that request as observed by the client.\nThis is the same number your users experience. It's the right number for an SLO.\n\n---\n\n## Output example\n\nGiven a HAR with requests to 6 routes:\n\n```javascript\n// Auto-generated by har-to-slo\n// https://github.com/aks-builds/har-to-slo\n\nexport const options = {\n  thresholds: {\n    // GET /api/v1/users/{id} — p95 baseline: 142ms (n=8, ×1.5)\n    \"http_req_duration{scenario:GET_/api/v1/users/_id_}\": [\"p(95)\u003c213\"],\n    // GET /api/v1/products — p95 baseline: 250ms (n=8, ×1.5)\n    \"http_req_duration{scenario:GET_/api/v1/products}\": [\"p(95)\u003c375\"],\n    // POST /api/v1/orders — p95 baseline: 610ms (n=8, ×1.5)\n    \"http_req_duration{scenario:POST_/api/v1/orders}\": [\"p(95)\u003c915\"],\n  },\n};\n```\n\n---\n\n## Using alongside grafana/har-to-k6\n\nThese tools do different things and work together:\n\n```bash\n# Step 1 — generate a k6 load test script from your HAR recording\nnpx har-to-k6 recording.har -o load-test.js\n\n# Step 2 — generate statistically grounded thresholds for that script\nnpx har-to-slo --input recording.har --output thresholds.js\n\n# Step 3 — merge thresholds.js into load-test.js\n```\n\n`har-to-k6` asks: *\"what requests should my load test replay?\"*\n`har-to-slo` asks: *\"what latency should I fail the load test at?\"*\n\n---\n\n## Options\n\n| Flag | Default | Description |\n|---|---|---|\n| `--input`, `-i` | *(required)* | Path to HAR file |\n| `--multiplier`, `-m` | `1.5` | Threshold = p95 × multiplier |\n| `--format`, `-f` | `js` | Output format: `js` or `json` |\n| `--output`, `-o` | stdout | Write to file instead of stdout |\n| `--explain`, `-e` | off | Annotate with Claude Haiku rationale (needs `ANTHROPIC_API_KEY`) |\n\n---\n\n## E2E test results\n\n| Dataset | Entries | Routes | Status |\n|---|---|---|---|\n| Small | 5 | 3 | ✅ |\n| Medium | 48 | 6 | ✅ |\n| Large | 500 | 10 (with UUIDs) | ✅ |\n\n---\n\n## Claude Code Skill\n\nA bundled Claude Code skill lets AI agents call `har-to-slo` during PR review to check whether load test thresholds are grounded in real data or invented.\n\n---\n\n## License\n\nMIT © [aks-builds](https://github.com/aks-builds)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faks-builds%2Fhar-to-slo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faks-builds%2Fhar-to-slo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faks-builds%2Fhar-to-slo/lists"}