-
Notifications
You must be signed in to change notification settings - Fork 0
docs: say what abusing the collection path costs #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # What abusing the collection path costs | ||
|
|
||
| The beacon reports to a path on the site's own domain, and CloudFront records the request in the | ||
| access log like any other. The path is open and unauthenticated. Anybody can send that URL a | ||
| million times and have every one of them counted, carrying a page value naming a page nobody opened and an event that | ||
| never happened. | ||
|
|
||
| Two things follow, and they want different answers. The counts recover. The money is spent. | ||
|
|
||
| ## Layer 1 is open in the same way | ||
|
|
||
| This comes first because the beacon looks like the thing that opened the door. | ||
|
|
||
| A site's own pages take a request from anybody. A million requests for a real page put a million | ||
| rows in the log, and the pageview count follows them up. Every analytics product built on server | ||
| logs works this way. A log records what arrived and has no way to ask why. | ||
|
|
||
| What layer 2 adds is a forged page value and events nobody caused. The gap is narrower than it | ||
| looks. A spammed page request already lies about which page was read, and it transfers the page body | ||
| to do it. A spammed beacon request carries no body in either direction. | ||
|
|
||
| ## The counts recover | ||
|
|
||
| The raw store is immutable and every rollup is rebuilt from it. A poisoned window is a re-run under | ||
| a better filter. | ||
|
|
||
| [#104](https://github.com/KensioSoftware/rainlytics/issues/104) carries that filter. It belongs in | ||
| the rollup query, beside the [crawler filter](../rollups/#crawlers-are-most-of-the-traffic) every | ||
| question already applies. The raw store keeps every row and the query decides what to count. A rule | ||
| that turns out to be wrong is another re-run. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Do not describe issue Issue 🤖 Prompt for AI Agents |
||
|
|
||
| The [log bucket's](../log-bucket/) expiry is the outer limit on this. A window that has aged past it | ||
| has no rows left to recount, under any filter at all. A year is the default. | ||
|
|
||
| ## The money is spent | ||
|
|
||
| A re-run fixes a number. Nothing re-runs a bill. Every spammed request buys three charges, and the | ||
| site pays all three whatever a rollup later decides about the row. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- docs/abuse/README.md ---'
sed -n '1,70p' docs/abuse/README.md
printf '%s\n' '--- docs/rollups/README.md ---'
sed -n '1,180p' docs/rollups/README.md
printf '%s\n' '--- referenced upstream document ---'
curl -L --fail --silent --show-error \
https://raw.githubusercontent.com/KensioSoftware/rainlytics/main/docs/rollups/README.md | sed -n '1,180p'Repository: KensioSoftware/rainlytics Length of output: 20556 Make the Athena charge conditional. Athena charges occur only when a rollup or 🧰 Tools🪛 LanguageTool[grammar] ~38-~38: Ensure spelling is correct (QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1) 🤖 Prompt for AI Agents |
||
|
|
||
| **A CloudFront request.** The distribution charges per request at its own rate, and that charge | ||
| lands on the CDN bill whether Rainlytics is installed or not. A request for a real page costs the | ||
| same and transfers a page body on top of it. Whatever answers the collection path is priced per hit | ||
| too, and [#99](https://github.com/KensioSoftware/rainlytics/issues/99) is choosing between a | ||
| CloudFront Function and a small cached object on those terms. | ||
|
|
||
| **A log record, kept for the bucket's retention.** | ||
| [#9](https://github.com/KensioSoftware/rainlytics/issues/9) measured the whole pipeline at $0.084 a | ||
| month on a site serving 137,000 requests a day, which works out near $0.02 per million requests. It | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- docs/abuse/README.md lines 35-52 ---'
sed -n '35,52p' docs/abuse/README.md
printf '%s\n' '--- GitHub issue `#9` ---'
curl -L --fail --silent --show-error \
-H 'Accept: application/vnd.github+json' \
https://api.github.com/repos/KensioSoftware/rainlytics/issues/9 \
| jq '{title, state, body, html_url}'Repository: KensioSoftware/rainlytics Length of output: 3150 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository references ---'
rg -n -C 3 '\$0\.084|`#9`|137,000|0\.02 per million' --glob '!package-lock.json' .
printf '%s\n' '--- GitHub issue `#9` comments ---'
curl -L --fail --silent --show-error \
-H 'Accept: application/vnd.github+json' \
'https://api.github.com/repos/KensioSoftware/rainlytics/issues/9/comments?per_page=100' \
| jq '[.[] | {user: .user.login, body: .body, html_url}]'Repository: KensioSoftware/rainlytics Length of output: 36721 Describe Issue 🤖 Prompt for AI Agents |
||
| splits between one PUT per delivered object and steady-state storage under the 365-day expiry. A | ||
| flood pays that on the way in and then pays the storage every month until the expiry drops it. | ||
|
|
||
| **Bytes that every query over the window scans.** Athena bills $5.00 per terabyte. Spammed rows sit | ||
| in the same objects as real ones and no partition predicate tells them apart. Every rollup covering | ||
| the window reads them once per run, for as long as that window stays in range. | ||
|
|
||
| The third charge already has a ceiling. The [query workgroup's](../query-workgroup/) | ||
| bytes-scanned cutoff fails a query at ten gibibytes, which caps one query near five cents whatever | ||
| the flood put in the window. The first two have no ceiling. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target excerpt ---'
sed -n '45,65p' docs/abuse/README.md
printf '%s\n' '--- linked workgroup document ---'
sed -n '1,180p' docs/query-workgroup/README.md
printf '%s\n' '--- workgroup configuration references ---'
rg -n -C 3 'workgroupName|rainlytics|bytes.?scanned|cutoff|primary' . \
-g '!node_modules' -g '!dist' -g '!build'Repository: KensioSoftware/rainlytics Length of output: 50384 🏁 Script executed: #!/bin/bash
set -eu
sed -n '45,65p' docs/abuse/README.md
printf '\n--- docs/query-workgroup/README.md ---\n'
sed -n '1,180p' docs/query-workgroup/README.md
printf '\n--- relevant references ---\n'
rg -n -C 3 'workgroupName|rainlytics|bytes.?scanned|cutoff|primary' docs src .github 2>/dev/null || trueRepository: KensioSoftware/rainlytics Length of output: 50384 🌐 Web query:
💡 Result: Based on your search query, it appears you are conflating two distinct concepts: KensioSoftware's Yulin library (an AWS simulator for testing) [1] and Amazon Athena's Workgroup features [2]. 1. KensioSoftware Yulin: This is a Node.js library used to simulate AWS service behavior locally for unit and integration testing [1]. There is no "rainlytics" project associated with KensioSoftware; the repository "yulin" contains documentation for simulated AWS services like S3, CloudFormation, and CloudFront [3][4][1]. 2. Athena Workgroups and "bytes scanned": Amazon Athena uses "workgroups" to isolate queries, manage access, and control costs [2][5]. Within a workgroup configuration, you can set a bytes-scanned cutoff per query (minimum 10 MB) to cancel queries that exceed a specific threshold of data scanned [6][2][7]. 3. Redshift Workgroups: You may also be encountering references to Redshift Serverless "WorkgroupName" in the context of querying "bytes scanned" [8][9][10]. Unlike Athena, where this is a built-in configuration limit [2], tracking bytes scanned in Redshift often involves querying system views like SVL_QUERY_METRICS_SUMMARY to calculate the data read during query execution [8][9]. There is no technical documentation or standard software project known as "rainlytics" that combines these parameters. It is likely a misspelling or a project-specific internal name. If you are developing a custom tool to manage Athena or Redshift cost limits, you would interact with the respective AWS APIs (e.g., Athena's CreateWorkGroup or Redshift Data API) rather than a package called rainlytics [8][11]. Citations:
State that the query cap requires the Rainlytics workgroup. The 10 GiB cutoff applies only when a query runs in the 🤖 Prompt for AI Agents |
||
|
|
||
| ## AWS WAF, and why it stays out of the default | ||
|
|
||
| WAF is the one place at the edge where a request count can be kept, and it is priced in the open. | ||
| Read from the AWS WAF pricing page on 2026-08-29: | ||
|
|
||
| - $5.00 a month per web ACL | ||
| - $1.00 a month per rule | ||
| - $0.60 per million requests inspected | ||
|
|
||
| A rate-based rule is an ordinary rule at $1.00. So the smallest configuration that would help, one | ||
| web ACL carrying one rate-based rule on the collection path, is $6.00 a month before a single request | ||
| reaches it. | ||
|
|
||
| Set that beside the $0.084 a month #9 measured. WAF is a fixed floor around seventy times the | ||
| pipeline it protects, and it is billed in full in a quiet month when nobody attacks anything. Every | ||
| other charge on this page is priced by use, and this would be the largest line on a quiet site's | ||
| bill. | ||
|
|
||
| That answer flips for a site already running a web ACL for other reasons. The $5.00 is paid, the | ||
| rule is $1.00, and the collection path joins something that exists. The default is for a site | ||
| installing Rainlytics, where the ACL would exist for this alone. | ||
|
|
||
| ## Why the count has to live in WAF | ||
|
|
||
| Rate limiting needs a count that survives between requests, and the edge has nowhere to keep one. | ||
|
|
||
| - **CloudFront Functions** hold no state between invocations. A function sees one request and | ||
| forgets it. | ||
| - **CloudFront KeyValueStore** is read-only from function code. A function reads what a deploy put | ||
| there and cannot write a counter back. | ||
| - **Shield Standard** comes at no charge and works at the network layer. Ten well-formed HTTPS | ||
| requests a second look like traffic to it. | ||
| - **Shield Advanced** carries the application-layer protection and costs $3,000 a month. | ||
|
|
||
| ## A budget alarm is the honest answer | ||
|
|
||
| An exposure that outlasts every attempt to prevent it is one to be told about. AWS Budgets gives an | ||
| account its first two budgets at no charge, and a cost alarm is one of them. | ||
|
Comment on lines
+105
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '88,102p' docs/abuse/README.mdRepository: KensioSoftware/rainlytics Length of output: 1132 🌐 Web query:
💡 Result: AWS Budgets offers a tiered pricing structure based on the functionality enabled for each budget [1]. Monitoring-only budgets, which track your costs or usage and send notifications when thresholds are exceeded or forecasted, are free of charge [1][2]. There is no limit to the number of these monitoring-only budgets you can create [3]. For budgets that include automated actions (such as applying IAM policies, Service Control Policies, or targeting EC2/RDS instances), the first two action-enabled budgets per month are free of charge [1][2]. This free allowance applies regardless of how many individual actions you configure within those two budgets [1]. Each additional action-enabled budget beyond the first two incurs a cost of $0.10 per day [1][4]. Additionally, AWS Budgets Reports, which provide scheduled performance monitoring, are charged at $0.01 per report delivered [1]. Citations:
Correct the AWS Budgets free-tier wording. Monitoring-only budgets are free. The first two action-enabled budgets are free, and later action-enabled budgets incur a charge. Distinguish these cases because this recommendation uses a monitoring alarm. 🤖 Prompt for AI Agents |
||
|
|
||
| Put one on the account carrying the distribution and the log bucket, with a threshold above what a | ||
| quiet month costs (#9's figure is the right shape for a site of that size, and a month of real | ||
| billing is better). An alert firing at twice a normal month is a flood in progress. The decision | ||
| about WAF is then taken with a bill in hand, which beats guessing at one during a deploy. | ||
|
|
||
| ## No WAF construct ships here | ||
|
|
||
| Every resource Rainlytics creates is priced by use, and a construct putting $6.00 a month into the | ||
| default path would break that for every site installing it. Whether the $6.00 is worth paying | ||
| depends on what a site is worth attacking, what else its account already runs, and what its owner | ||
| wants to spend. That is the site's decision, and it is taken with information the library lacks. | ||
|
|
||
| A site taking it writes the web ACL in its own CDK app and associates it with the distribution. The | ||
| collection path is `/_rainlytics` unless a site names another, and it is exported as | ||
| `defaultBeaconPath` from the package root, so a rate-based rule can scope itself to the same path | ||
| the beacon reports to. | ||
|
|
||
| <!-- card | ||
| ```text | ||
| one web ACL and one rate-based rule $6.00 a month | ||
| the pipeline #9 measured $0.084 a month | ||
| a budget alarm watching for a flood no charge | ||
| ``` | ||
| --> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Qualify the Layer 1 pageview example.
The rollup excludes crawler user agents by default and counts only successful HTML
GETrequests. One million page requests do not automatically become one million pageviews. Change “the pageview count follows them up” to “can inflate the pageview count when the requests pass the pageview predicate.” (raw.githubusercontent.com)🤖 Prompt for AI Agents