Synthesis Engineering · Synthesis Coding · Synthesis Writing · Synthesis Project Management

The architecture of an open-source slop detector

A free-to-use tool that proxies user-submitted content to commercial LLM providers has two failure modes that kill it within a week of public launch. The first is cost runaway from automated abuse: somebody points a script at the endpoint and the daily provider bill exceeds the budget. The second is prompt injection that turns the tool into a different tool: a submission that overrides the system prompt and uses your provider key to do work you did not authorize.

Most “free” AI tools handle these by adding a paywall, harvesting data to monetize, or going dark when the bill arrives. The fourth option (stay free, stay private, stay open, stay alive) is the architecture problem the engineering for slopcheck had to solve. The choices that made it work are reusable for anyone building in the same shape, and this post is the technical companion to the canonical launch piece on synthesiswriting.org and the methodology piece on synthesisengineering.org.

The architecture has four load-bearing pieces: the two-axis detection discipline, the multi-pass orchestrator, the seven-layer hosted-tier safety, and the tools-router pattern. Each is a separable decision and each can be ported into a different system that has the same shape.

Two-axis discipline as an engineering argument

Detection systems that emit a single score conflate two questions. The first is provenance: which family of model produced this text, and how confidently can the system claim that. The second is quality: does the text carry load-bearing claims, or is it structurally empty regardless of authorship. A single score forces the two questions into one ratio and produces calibration headaches that get worse as models converge stylistically.

The two-axis discipline is the engineering response. The orchestrator runs two separate analyses on every submission: one against the model-family fingerprinting catalog (signal strength when present, base rate by family, combined-signal fingerprints), one against the substance-and-depth catalog (the deletion test, the any-company test, load-bearing claim count, the seventeen sub-patterns in section A2 of the methodology). The outputs surface as separate fields in the response.

The two-axis approach has a concrete engineering benefit beyond the framing: it makes the ESL safe-harbor implementable as a first-class rule. Liang et al. (arxiv 2304.02819, 2023) showed that the cornerstone AI signature (uniform paragraphs + restricted vocabulary + heavy transitions) is also the cornerstone signature for non-native English writing. A single-score detector cannot reliably exclude that case. A two-axis detector can refuse to elevate the AI-signal score when the cornerstone fires without any register-specific AI marker also present.

Multi-pass orchestrator

LLM providers have context-window limits. The slopcheck methodology runs to roughly forty pages of pattern catalogs plus example output. For most submissions the full catalog plus the user content fits in the context window of a single call. For long submissions (research-paper-length content, multi-thousand-word manuscripts), it does not.

The orchestrator picks single-pass or multi-pass at runtime. Single-pass is the default: the catalog ships in the system prompt, the user content fills the user turn, the model emits structured output. Multi-pass triggers when the input would push the request over the context budget. In multi-pass, the orchestrator slices the catalog into focused subsets (model-family fingerprinting in one pass, substance-and-depth in a second, cross-cutting calibration in a third), runs each pass with only the relevant catalog section in scope, and aggregates the structured outputs into the same response shape the single-pass path produces.

The switch rule lives in the orchestrator and is observable in the response metadata. The rule itself is not “always multi-pass to be safe” (that would burn three calls instead of one for the common case) and is not “always single-pass” (that would silently drop catalog sections when the content is long). The rule is conditional on measured token count, with a configurable headroom margin to absorb prompt-template overhead.

The structured output contract is fixed across pass strategies. Downstream consumers (the web app UI, the CLI, the integrations) cannot tell whether a given response came from a single-pass or a multi-pass run unless they look at the metadata. This is the property that makes the orchestrator a swap-in upgrade rather than a parallel codepath.

Hosted-tier seven-layer safety

The hosted tier is the piece that turns the tool from “BYOK only, you provide your own provider key” into “free for anyone, no signup, no account, no key required.” The hosted tier costs me money per request; the safety layers exist so it costs me a bounded amount rather than whatever an abuse script wants it to cost.

Layer one: provider caps. Each provider account (Anthropic, OpenAI, Google) has a per-account daily spend cap set in the provider dashboard. If a layer below this one fails, the provider cap fails the request before it bills. The cap is not a budget; it is a hard ceiling on worst-case loss.

Layer two: the Pages Function as proxy. The browser never sees the provider key. The web app calls a Cloudflare Pages Function at tools.synthesiswriting.org/slopcheck/api/hosted/analyze. The Function holds the provider keys in environment secrets set via wrangler pages secret put, calls the provider on the user’s behalf, and returns the structured response. The deployed implementation diverged from the original spec (which had a standalone Worker) because co-locating the Function with the static frontend simplified deployment without losing isolation.

Layer three: hashed-IP rate limit. The Function reads the client IP from the Cloudflare-injected header, hashes it (no plaintext stored), and looks up the daily request count in a Cloudflare KV namespace. If the count exceeds the per-IP daily allowance, the Function returns a 429 with a message pointing at the BYOK path. No PII is stored; the KV entry is the hash and a count.

Layer four: daily kill switch. A KV-backed flag plus a daily community budget. When the community budget is consumed for the day, the Function returns a friendly message asking the requester to use BYOK and resets at midnight UTC. The kill switch is the trapdoor under all the other layers: if every other layer is misconfigured, the global flag stops the bleed.

Layer five: user-content size cap. The hosted-tier path accepts submissions up to 200,000 characters. Above that, BYOK is the path. The cap protects both the provider call cost (token count scales with character count) and the latency budget. The cap is enforced in the Function before the provider call.

Layer six: model allowlist. The Function only accepts requests for specific provider/model combinations from the allowlist. A request for a model the allowlist does not include is rejected before the provider call. The allowlist is a configuration file in the repo, not a database table, so the audit trail lives in git rather than in a runtime store.

Layer seven: Turnstile plus the internal-proxy-key bypass. The web app gates submissions with Cloudflare Turnstile, which raises the cost of automated abuse without imposing CAPTCHAs on legitimate users. The internal integrations (the email-in worker, the Slack bot, the Discord bot, the Google Docs add-on) bypass Turnstile by presenting an internal-proxy-key header. The key is rotated and is not exposed to client code; the integration codepaths run server-side with the key set in their own environment.

The layers are not redundant in a wasteful sense; each catches a different failure mode. A layer-one provider cap does not stop prompt injection. A layer-five size cap does not stop a script that submits 1,000 small requests per minute. A layer-three rate limit does not stop a coordinated distributed attempt. Defense in depth is the only stance that survives contact with a production tool that is free, public, and worth abusing.

Tools-router pattern

The slopcheck tool lives at tools.synthesiswriting.org/slopcheck/. The subdomain is tools.synthesiswriting.org; the path is /slopcheck/. Adding a second tool under the same subdomain means adding a path, not a subdomain. This is a small choice with compounding payoff.

The implementation is a Cloudflare Worker (the tools-router) that fronts the subdomain. The Worker reads the path and proxies to the appropriate underlying service: the Pages site for /slopcheck/, separate services for any other tool added later. The Worker is the indirection layer that lets the underlying services be Pages projects, standalone Workers, external services, or anything else with an HTTPS endpoint.

Adding a new tool under tools.synthesiswriting.org/<name>/ requires three changes: a new path entry in the Worker, a new Pages project or service that backs the path, and DNS already configured for the subdomain (a one-time setup that the Worker inherits). The alternative pattern (one subdomain per tool) requires DNS, certificate issuance, and a separate routing decision per tool, which adds friction every time and rewards adding fewer tools.

License posture and repo hygiene

Three repo-level choices matter for an open-source launch and would have killed the project if I had skipped any of them. They live at the bottom of this post because they read as paperwork until the moment they matter, at which point they read as load-bearing.

First, clean-history at public flip. The three primary repositories (slopcheck, tools-router, homebrew-tap) had their commit histories rewritten to a single Initial commit before they were flipped from private to public. The rewrite removed the iteration history while preserving the final state. The cost is real: contributors cannot see how the code evolved. The cost is worth paying because the iteration history is not a teaching artifact and would have surfaced early editorial decisions out of context.

Second, no force-push during development on protected repos. Force-push is allowed and used during the pre-public iteration where the history is being shaped. Once a repo is public, force-push is off. The trade-off is that a bad commit is fixed with a new commit, not by rewriting history; the history readers see is the history I am willing to defend.

Third, FUNDING.yml across every public repo. The file lives at .github/FUNDING.yml in each repository and points GitHub’s Sponsor button at the same destination. The policy is consistent across the synthesis ecosystem and the personal Github profile: one canonical sponsorship destination, surfaced uniformly, never a paywall.

What this enables

The architecture is not novel in any individual layer; the multi-pass orchestrator pattern is well-known, the seven-layer safety stance is recognizable from any decent threat-model writeup, the tools-router pattern is a subdomain-routing classic, the clean-history flip is operationally standard. The contribution is the integration: these specific choices together produce a tool that is free, private, open, and operationally bounded.

For readers thinking about building something in the same shape (a free public AI-mediated tool with privacy by default), the layers are the starting checklist. For readers thinking about the detection methodology that drives the analysis (the compounding archive, the fourteen-field per-pattern template, the two-axis calibration), the methodology piece on synthesisengineering.org is the deeper read. For readers who want to see what the tool does without reading any more architecture, the web app at tools.synthesiswriting.org/slopcheck/ is the shortest path to the running tool.

Source for everything lives at github.com/synthesisengineering: the synthesis-slopcheck repo (web app, hosted-tier Function, integrations, CLI, browser extension), the synthesis-tools-router repo (the subdomain Worker), the synthesis-skills repo (the methodology catalog), and the homebrew-tap repo (the macOS package installer).

Originally published on rajiv.com
AIarchitectureopen sourceCloudflareslopcheck