{"id":20290633,"url":"https://github.com/gocardless/slo-builder","last_synced_at":"2026-03-07T01:34:19.461Z","repository":{"id":42388357,"uuid":"196885518","full_name":"gocardless/slo-builder","owner":"gocardless","description":"Templates for building SLOs with Prometheus rules and alerts","archived":false,"fork":false,"pushed_at":"2023-07-13T11:49:03.000Z","size":4699,"stargazers_count":16,"open_issues_count":2,"forks_count":6,"subscribers_count":58,"default_branch":"master","last_synced_at":"2025-04-11T11:48:12.160Z","etag":null,"topics":["observability","prometheus"],"latest_commit_sha":null,"homepage":"","language":"Go","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/gocardless.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-07-14T21:33:42.000Z","updated_at":"2024-09-23T21:25:56.000Z","dependencies_parsed_at":"2023-02-01T00:45:38.172Z","dependency_job_id":null,"html_url":"https://github.com/gocardless/slo-builder","commit_stats":{"total_commits":44,"total_committers":10,"mean_commits":4.4,"dds":0.4545454545454546,"last_synced_commit":"de45acfcb576360e68ca1711d171ce214c3245f2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gocardless/slo-builder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fslo-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fslo-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fslo-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fslo-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gocardless","download_url":"https://codeload.github.com/gocardless/slo-builder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fslo-builder/sbom","scorecard":{"id":433128,"data":{"date":"2025-08-11","repo":{"name":"github.com/gocardless/slo-builder","commit":"de45acfcb576360e68ca1711d171ce214c3245f2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":5,"reason":"Found 9/18 approved changesets -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/test.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/gocardless/slo-builder/test.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/gocardless/slo-builder/test.yaml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/gocardless/.github/SECURITY.md:1","Info: Found linked content: github.com/gocardless/.github/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: github.com/gocardless/.github/SECURITY.md:1","Info: Found text in security policy: github.com/gocardless/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 25 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":5,"reason":"5 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0493 / GHSA-p782-xgp4-8hr8","Warn: Project is vulnerable to: GO-2021-0061 / GHSA-r88r-gmrh-7j83","Warn: Project is vulnerable to: GO-2022-0956 / GHSA-6q6q-88xp-6f2r","Warn: Project is vulnerable to: GO-2020-0036 / GHSA-wxc4-f4m6-wwqv","Warn: Project is vulnerable to: GO-2022-0322 / GHSA-cg3q-j54f-5p7p"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T03:51:40.358Z","repository_id":42388357,"created_at":"2025-08-19T03:51:40.359Z","updated_at":"2025-08-19T03:51:40.359Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30205164,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"ssl_error","status_checked_at":"2026-03-06T18:57:34.882Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["observability","prometheus"],"created_at":"2024-11-14T15:08:33.538Z","updated_at":"2026-03-07T01:34:19.414Z","avatar_url":"https://github.com/gocardless.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# slo-builder [![Documentation](https://godoc.org/github.com/gocardless/slo-builder/pkg/templates?status.svg)](http://godoc.org/github.com/gocardless/slo-builder/pkg/templates)\n\nThis repo provides a framework that developers can use to specify system SLOs\nwithout requiring in-depth Prometheus knowledge.\n\n## Installation\n\nRun the following command **outside of a `go.mod` repository** to install the `slo-builder` binary.\n\n```\ngo install github.com/gocardless/slo-builder/cmd/slo-builder@latest\n```\n\nYou also need to ensure your `go/bin` directory is available in your `$PATH`:\n\n```bash\nexport PATH=\"$HOME/go/bin:$PATH\"\n```\n\n## Examples\n\nSee [`example-definitions.yaml`](./example-definitions.yaml) and [`example-rules.yaml`](./example-rules.yaml) for full examples of all available SLO templates.\n\n## Why?\n\nSLOs are often formulated in business terms first, then translated into\nmonitoring system rules. Good SLOs should be formed as a ratio of good events to\ntotal events, and come with an associated error budget- the margin of error\nyou'd expect to consume in normal operation.\n\nBy forcing a homogenous format for every SLO, it becomes possible to apply\ngenerically useful rules to all different types of SLO. This is even more\nimportant when the implementation of such rules are so tricky, and the required\nlearning to produce them so large.\n\n## Steps to an SLO\n\nYou start with a system, often with a number of SLIs. You then:\n\n1. Formulate SLOs in business terms\n2. Implement the SLOs in the monitoring system (Prometheus)\n3. Write multi-window alerts for burning error budgets\n\nSystem components will need different categories of SLO: an SLO for HTTP\nrequests will be structured differently than a batch processing system, for\nexample. This framework offers a collection of predefined templates that map to\ndifferent types of system, and can help someone unfamiliar with SLOs quickly\nproduce rules that Just Work.\n\nWhen formulating SLOs in business terms (1), you can use these predefined\ntemplates to help inform your selections. This framework can then produce the\nrules (2) that generate a common input to multi-windowed alerts (3), which are\nincluded at the end of this SLO pipeline.\n\n## `BaseSLO`\n\nEvery SLO has some common behaviour, as represented by the `baseSLO` type. An\nexample core SLO definition would be:\n\n```go\nBaseSLO{\n  Name: \"MarkPaymentsAsPaidMeetsDeadline\",\n  ErrorBudget: 0.1,\n}\n```\n\nIn rule form, we produce a `job:slo_definition:none` rule which tracks the\nparameters of the base SLO. This writes a time-series that can be inspected for\nhow the SLO definition changed with time.\n\nWe also produce a `job:slo_error_budget:ratio` which will be used at the end of\nthe SLO pipeline to apply alerting rules. Each of these rules has the `name`\nlabel that is assumed to be unique to each SLO, allowing Prometheus to join\nseries on the `name` label.\n\n```\njob:slo_definition:none{name=\"MarkPaymentsAsPaidMeetsDeadline\",error_budget=\"0.1\"} 1.0\njob:slo_error_budget:ratio{name=\"MarkPaymentsAsPaidMeetsDeadline\"} 0.1\n```\n\n## `BatchProcessingSLO`\n\nWe'll use an example of a process that transitions many payments into a paid\nstate as a batch process for which we want to apply an SLO.\n\n```go\nMarkPaymentsAsPaidMeetsDeadline = BatchProcessingSLO{\n  BaseSLO:  BaseSLO{\"MarkPaymentsAsPaidMeetsDeadline\", 0.1},\n  Deadline: time.Duration(2) * time.Hour,\n  Volume: `\n  1.5 * max_over_time(\n    (\n      sum by (namespace, release) (\n        increase(paysvc_mark_payments_as_paid_marked_as_paid_total[8h])\n      )\n    )[60d:1h]\n  )`,\n  Throughput: `\n  sum by (namespace, release) (\n    rate(paysvc_mark_payments_as_paid_marked_as_paid_total[1m])\n  ) \u003e 0`,\n}\n```\n\nUsers provide a time by which an entire batch must complete, along with an\nestimation of max volume and a current measurement of throughput. This is enough\ninformation to infer a target throughput (knowing how fast items need processing\nto hit the deadline) which can be used to score each minute of activity from the\njob.\n\nIn total, we produce three rules for this specific SLO:\n\n```\njob:slo_batch_volume:max{name=\"MarkPaymentsAsPaidMeetsDeadline\",namespace=\"production\",release=\"paysvc-live\"}\t2058877.53186143\njob:slo_batch_throughput_target:max{name=\"MarkPaymentsAsPaidMeetsDeadline\",namespace=\"production\",release=\"paysvc-live\"} 285.95521275853196\njob:slo_batch_throughput:interval{name=\"MarkPaymentsAsPaidMeetsDeadline\",namespace=\"production\",release=\"paysvc-live\"} 369.7091257289802\n```\n\nThese rules are then consumed by a generic batch processing rule that translates\ninto `job:slo_error:ratio\u003cI\u003e` rules for each common alert window. The most\nimportant rule is the generation of an error 'score' for the batch process,\nwhich looks like:\n\n```yaml\n- record: job:slo_batch_error:interval\n  expr: |\n    1.0 - clamp_max(\n      job:slo_batch_throughput:interval / job:slo_batch_throughput_target:max,\n      1.0\n    )\n```\n\nIn this context, `job:slo_batch_error:interval` is the error score for each\ninterval of the throughput given in the SLO specification. For\nMarkPaymentsAsPaid, with the target throughput of 285 payments/s calculated over\na 1m window, you'd have the following scores:\n\n| Throughput | `job:slo_batch_throughput_target:max` | `job:slo_batch_error:interval` |\n| --- | --- | --- |\n| 0 | 285 | 100% |\n| 100 | 285 | 65% |\n| 285 | 285 | 0% |\n| 300 | 285 | 0% |\n\nThis means you burn your error budget when the batch job performs below the\ntarget throughput, and the rate at which you burn it is dependent on how\nsignificantly you fail to meet it. It's also important to note that minutes\nwhere the throughput greatly exceeds the target don't 'recoup' error budget-\nthis is an implementation decision, and might be the wrong choice.\n\n## Alerting\n\nEvery SLO template conforms to our definition of an SLO, which is something that\nhas a name, associated error budget and a constantly refreshed error ratio. In\nPrometheus terms, that means your SLOs will eventually produce the following\ntime series:\n\n- `job:slo_error:ratio1m`\n- `job:slo_error:ratio5m`\n- `job:slo_error:ratio30m`\n- `job:slo_error:ratio1h`\n- `job:slo_error:ratio2h`\n- `job:slo_error:ratio6h`\n- `job:slo_error:ratio1d`\n- `job:slo_error:ratio7d`\n\nAs we get these series for every SLO, we can write generic alerting rules that\nwork across any SLO. It happens that building useful alerts on SLO measurements\nis more complex than it might seem, and leveraging generic alerts is a huge\nbenefit for simplicity.\n\nWe use a combination of the [SRE\nworkbook](https://landing.google.com/sre/workbook/chapters/alerting-on-slos/)\nand [SoundCloud: Alerting on SLOs like\nPros](https://developers.soundcloud.com/blog/alerting-on-slos) to form\nmulti-window error budget burn alerts. The term 'multi-window' indicates that\nalerts are only triggered when error budget is being burned in both short and\nlong-term intervals: this reduces alert false positives and improves alert reset\ntime, causing alerts to resolve as soon as the problem has been corrected\ninstead of hours after.\n\nDepending on the urgency of the detected error, we'll either page an on-call\nengineer or open a ticket to handle the error budget burn in business hours. The\ndetection sensitivity windows are listed here:\n\n| Alert | Long Window | Short Window | `for` Duration | Burn Rate Factor | Error Budget Consumed |\n| --- | --- | --- | --- | --- | --- |\n| Page | 1h | 5m | 2m | 14.4 | 2% |\n| Page | 6h | 30m | 15m | 6 | 5% |\n| Ticket | 1d | 2h | 1h | 3 | 10% |\n| Ticket | 3d | 6h | 1h | 1 | 10% |\n\nEvery SLO created with this framework is automatically subscribed to these\nalerts. Where they get routed- both who is paged, and where a ticket gets\ncreated- depends on the team assigned to the SLO.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fslo-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgocardless%2Fslo-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fslo-builder/lists"}