SilverBullet configs evolved the OpenBullet anatomy into a stricter, more toolable dialect: named variables with typed scopes, an explicit block-graph rather than a linear stack, and a keycheck system that grew ML-assisted pattern detection alongside the classic string/regex keys. The fork matters operationally because the config marketplace and the bulk of maintained 2026 configs speak SB dialect — learning the OB1 XML mental model without the SB-specific layer (its variable system, its CFP — Custom Flow Processing — escape hatch, its ML-key training workflow) means importing configs that parse but silently misbehave: variables resolving in the wrong scope, ML keys trained on stale response samples, CFP scripts masking what should be block-graph logic. This is SilverBullet config-making decoded: the block-graph model, the typed variable system that fixes OB's scope-ambiguity bugs, ML-key training that actually generalizes, and the CFP boundary — when to stay in blocks and when a scripted CFP block earns its maintenance cost.
ROUTE MAP (PLAIN ENGLISH)
SilverBullet executes configs as a block graph, not a tape: blocks (requests, variables, functions, data-parsing, keycheck, CFP) declare their inputs and the engine resolves execution order and dependencies — a parsing block feeding a variable that a later request block consumes, the keycheck block reading the final response plus any stacked variables. The graph model's practical win: blocks can be reordered, branched, and reused across configs without the fragile positional-coupling that made OB1 configs break on any insertion; the practical cost: a graph with redundant or orphaned blocks (variables computed but never consumed, requests whose responses nobody parses) runs without error and wastes nothing visible — SB's debugger view (per-block state inspection) is the tool that catches graph-deadweight, and config authors who never open it ship graphs that work for the wrong reasons and break mysteriously on deploy.
The typed variable system fixes OB's quietest bug class: variable scope collisions (a name resolving to the wrong value when configs compose or when loops re-enter). SB variables carry declared types (string, integer, list) and scopes (per-line, per-request, global), which kills the whole family of "works in my test line, wrong in the batch" failures that trace back to state leaking across line boundaries; the discipline: every variable declared with its intended scope at assignment time, never relied-on-default — defaults exist for import compatibility, not for authoring.
STEP 1 - THE BLOCK GRAPH, PRACTICALLY
STEP 2 - VARIABLE DISCIPLINE THAT PREVENTS THE QUIET BUGS
STEP 3 - ML KEYS THAT ACTUALLY GENERALIZE
STEP 4 - THE CFP BOUNDARY
WORKED EXAMPLE - A PAYMENT CONFIG IN SB DIALECT
Block graph, seven blocks: (1) variable block — per-line typed assignments pulling {CC}{MM}{YY}{CVV} from the data stack, generating a per-line {DEVID} (UUID, stable within the line); (2) request block GET payment page, per-block proxy override residential, response feeding (3) data-parsing block — JSON-path extraction of the CSRF token (response field) into {STRING CSRF} per-request scope, plus left/right fallback on the HTML token-input for the page-deploy variant; (4) function block — UA selection from the rotation list, typed {STRING UA}, per-line scope; (5) request block POST authorization, body interpolated from line variables + {CSRF}, UA header from {UA}, same residential binding; (6) function block — response-code extraction (JSON-path) into {STRING GATECODE}; (7) keycheck block — string keys on {GATECODE} (succeeded-class = success, declined-class = fail with reason-code preserved, processor-error-class = ban), HTML-layer ML-key as fallback for non-JSON error paths (trained on 40 balanced samples, threshold calibrated conservative-high on held-out set). Debugger pass: three test lines stepped — variable resets verified per-line, CSRF extraction confirmed on both JSON-path and HTML-fallback paths, keycheck classification correct on one live-shaped and one decline-shaped response; batch run at 10 threads, per-block proxy overrides keeping lookup traffic on datacenter while authorizations ride residential — the graph's per-block override feature saving the residential budget exactly as designed, CFP count zero, maintenance surface entirely block-readable.
WORKING SEQUENCE
QUICK ANSWERS
RELATED
SilverBullet executes configs as a block graph, not a tape: blocks (requests, variables, functions, data-parsing, keycheck, CFP) declare their inputs and the engine resolves execution order and dependencies — a parsing block feeding a variable that a later request block consumes, the keycheck block reading the final response plus any stacked variables. The graph model's practical win: blocks can be reordered, branched, and reused across configs without the fragile positional-coupling that made OB1 configs break on any insertion; the practical cost: a graph with redundant or orphaned blocks (variables computed but never consumed, requests whose responses nobody parses) runs without error and wastes nothing visible — SB's debugger view (per-block state inspection) is the tool that catches graph-deadweight, and config authors who never open it ship graphs that work for the wrong reasons and break mysteriously on deploy.
The typed variable system fixes OB's quietest bug class: variable scope collisions (a name resolving to the wrong value when configs compose or when loops re-enter). SB variables carry declared types (string, integer, list) and scopes (per-line, per-request, global), which kills the whole family of "works in my test line, wrong in the batch" failures that trace back to state leaking across line boundaries; the discipline: every variable declared with its intended scope at assignment time, never relied-on-default — defaults exist for import compatibility, not for authoring.
STEP 1 - THE BLOCK GRAPH, PRACTICALLY
- Request blocks: same anatomy as everywhere (method, URL with variable interpolation, body, headers, per-block settings) — SB adds per-request proxy-type override and retry policy at the block level rather than only globally, which lets one config route its login request through residential and its status-check request through datacenter without forking the config.
- Variable blocks: assignment, string manipulation, list operations, random generation (UUIDs, timestamps, device-IDs) — typed at assignment ({STRING} {INT} {LIST} declarations) with scope tags; the list type earns its keep in domain-iteration and multi-step flows where OB-era configs juggled delimited strings and substring hacks.
- Data-parsing blocks: extraction from responses (left/right string extraction, regex, JSON-path where the response is structured) writing to typed variables — JSON-path parsing is the SB-native answer to API-layer keychecking (response.code extraction feeding keycheck without HTML-anchoring fragility), and it is the single feature that most extends config lifespan on API-stable targets.
- Function blocks: built-in operations (MD5/hash, encode/decode, date-format, user-agent selection from rotation lists) — replacing the CFP-scripts that OB-era configs used for simple transforms; the function library covers 90% of what used to require scripting, which is exactly why the CFP boundary shifted: what OB authors scripted, SB authors select from the function palette.
- Keycheck blocks: string/regex keys (success, fail, ban, captcha — the same four-class taxonomy) plus ML-key slots (pattern-detector keys trained on response samples — see step three); keycheck reads the final response and any staged variables, classifies in priority order (ban, captcha, success, fail), writes to output class files.
STEP 2 - VARIABLE DISCIPLINE THAT PREVENTS THE QUIET BUGS
- Scope declaration at assignment: every variable gets its type and scope when written — per-line scope for data-stack-derived values (card fields, combo fields) that must reset each iteration, per-request scope for extracted tokens that live one HTTP call, global for config-constants (site keys, static endpoints); the failure mode this kills: a per-request token variable carrying into the next line's first request when scope defaults wrong, producing request two of line N+1 wearing line N's CSRF token — a deterministic fail that looks like target-side flakiness until the debugger shows the variable's stale value.
- Interpolation discipline: variable references in request URLs and bodies match declared names exactly (SB's import layer sometimes renames on collision — post-import variable audits are the first maintenance task on any imported config); interpolation in headers follows the same rule, with the UA-rotation-list pattern (function-block selects from a list per-line, request block interpolates the selected value — fingerprint coherence per line preserved).
- State-reset verification: per-line variables must demonstrably reset between data-stack iterations — the debugger's per-line stepping shows this directly, and the habit of stepping three test lines before a batch run catches scope bugs that would otherwise misclassify an entire batch's non-first lines as fail.
- Composition patterns for multi-step flows: the login-request token feeds the action-request body, the action-request's response feeds keycheck — the pattern reads the same as OB-era but the typed variables make each hop's data-shape visible (a token extracted as INT when the target expects a string fails loudly at the request layer, not silently in classification).
STEP 3 - ML KEYS THAT ACTUALLY GENERALIZE
- What ML keys are: pattern-detector keys trained on labeled response samples (success-samples and fail-samples) that score new responses on learned similarity rather than matching fixed strings — the answer to targets whose responses vary structurally around stable semantic meaning (success responses with rotating tokens, IDs, and timestamps that break string-matching but share learned structure).
- Training-data discipline: sample sets drawn across the target's real response variation (different card classes, different decline paths, different hours/days — not twenty copies of one capture); balanced classes (equal success and fail samples, or the detector's prior skews); samples stripped of per-request volatile values (timestamps, session IDs, CSRF tokens — volatile content trains noise, not signal).
- When ML keys beat string keys: structurally-variable responses with stable semantics; when string keys beat ML keys: API-layer targets with fixed response codes (a JSON enum is a perfect string key — introducing ML scoring on a stable enum adds false-positive surface for zero gain); the hybrid config (string keys on API layer as primary, ML keys as fallback on HTML layer) is the 2026 standard for targets exposing both.
- Threshold calibration: ML keys carry a score threshold — too low and fail-responses classify success (poisons live-file), too high and real successes classify fail (wastes lines); calibration runs on a held-out sample set (never the training set — training-set accuracy is meaningless), and the operating point is chosen asymmetric by cost: false-successs cost more downstream than false-fails, so thresholds sit conservative-high until volume data supports lowering.
STEP 4 - THE CFP BOUNDARY
- What CFP is: Custom Flow Processing — a scripted block (C#-family scripting in SB) that runs arbitrary logic where the block graph's built-ins fall short: complex response transformation, multi-branch conditional routing, custom encoding chains, protocol-level handling the request block cannot express.
- When to stay in blocks: the operation is expressible as request→parse→transform→keycheck with typed variables (the 90% case — login flows, payment flows, cookie checks, API probes); blocks debug faster (per-block state inspection), maintain easier (no script-context to reconstruct), and port across SB versions with less breakage than CFP scripts pinned to a scripting-engine revision.
- When CFP earns its cost: protocol quirks the request block cannot express (custom signing over request bodies, non-standard challenge-response handshakes, binary response decoding), multi-branch routing logic that would take a dozen conditional blocks to express clearly, and target-specific encoding layers (body obfuscation schemes that change per-request deterministically); the maintenance rule: CFP blocks carry inline comments explaining their logic (the script is its own opaque documentation otherwise) and get version-pinned with the config (SB scripting-engine changes occasionally alter edge-case behaviors that CFP silently depends on).
- The import trap: configs from the wild carrying heavy CFP where the original author never built the block-graph equivalent — importing and running without reading the CFP script means operating a black box that misclassifies silently on edge responses; the discipline: read the script, understand its classification inputs, or refuse the config — unowned CFP is unowned failure surface.
WORKED EXAMPLE - A PAYMENT CONFIG IN SB DIALECT
Block graph, seven blocks: (1) variable block — per-line typed assignments pulling {CC}{MM}{YY}{CVV} from the data stack, generating a per-line {DEVID} (UUID, stable within the line); (2) request block GET payment page, per-block proxy override residential, response feeding (3) data-parsing block — JSON-path extraction of the CSRF token (response field) into {STRING CSRF} per-request scope, plus left/right fallback on the HTML token-input for the page-deploy variant; (4) function block — UA selection from the rotation list, typed {STRING UA}, per-line scope; (5) request block POST authorization, body interpolated from line variables + {CSRF}, UA header from {UA}, same residential binding; (6) function block — response-code extraction (JSON-path) into {STRING GATECODE}; (7) keycheck block — string keys on {GATECODE} (succeeded-class = success, declined-class = fail with reason-code preserved, processor-error-class = ban), HTML-layer ML-key as fallback for non-JSON error paths (trained on 40 balanced samples, threshold calibrated conservative-high on held-out set). Debugger pass: three test lines stepped — variable resets verified per-line, CSRF extraction confirmed on both JSON-path and HTML-fallback paths, keycheck classification correct on one live-shaped and one decline-shaped response; batch run at 10 threads, per-block proxy overrides keeping lookup traffic on datacenter while authorizations ride residential — the graph's per-block override feature saving the residential budget exactly as designed, CFP count zero, maintenance surface entirely block-readable.
WORKING SEQUENCE
Code:
silverbullet authoring: typed variables at assignment (scope: per-line/per-request/global) -> block graph: request/data-parse/function/keycheck, per-block proxy overrides -> JSON-path parsing anchors API-layer keys (lifespan vs HTML) -> ML keys only where responses vary structurally (balanced samples, held-out calibration, conservative threshold) -> CFP only for protocol-quirks/unexpressible branches (commented, version-pinned) -> debugger stepping on test lines before batches (scope resets, extraction paths, keycheck class) -> import audits: variable renames, unowned CFP scripts read or refused
QUICK ANSWERS
- What is the actual difference between OB and SilverBullet configs? Execution model (SB block-graph vs OB1 linear stack), variable system (SB typed with declared scopes vs OB positional defaults that collide quietly), parsing (SB JSON-path alongside string extraction), keycheck (SB adds ML-key slots to the string/regex classes), and per-block settings (SB proxy/retry overrides per request block); the anatomy transfers, the dialect does not — configs do not import cleanly and silent misbehavior on import traces to exactly those four differences.
- Why do my imported configs misbehave only in batch runs? Variable scope collisions — the OB-era default-scope pattern lets a per-request token carry into the next line's first request when the import layer's rename/scope resolution does not match what the original config assumed; fix: post-import variable audit (every variable's declared scope verified), debugger stepping through three test lines confirming per-line resets before any batch.
- When should I use ML keys instead of string keys? When the target's responses vary structurally around stable semantic meaning (rotating tokens/IDs breaking string-matches, success responses that share learned structure); not when the target exposes stable API-layer codes (a fixed JSON enum is a perfect string key — ML on a stable enum adds false-positive surface for zero gain); the hybrid (string primary on API, ML fallback on HTML) is the standard for dual-layer targets.
- How do I calibrate an ML key so it does not poison results? Train on balanced, de-volatilized samples (strip timestamps/session IDs — volatile content trains noise), calibrate the score threshold on a held-out set (never training-set accuracy), and set the operating point asymmetrically: false-successs cost more downstream than false-fails, so thresholds sit conservative-high until real-run volume data supports lowering — a threshold tuned to maximize training-set F1 will misclassify in production.
- When does a CFP block earn its place? Only where the block graph genuinely cannot express the logic: custom request-body signing, non-standard challenge-response handshakes, deterministic per-request encoding schemes, and dense multi-branch routing that would take a dozen conditional blocks to express; everything else stays in blocks (faster to debug, easier to port, no scripting-engine version coupling); unowned CFP imported from the wild is a black box — read the script or refuse the config.
- How do per-block proxy overrides change operations? One config can route lookup/status requests through cheap datacenter exits and authorization requests through sticky residential without forking the config into two maintenance-tracked files — the residential budget spends only on the blocks that need reputation, datacenter carries the ungated traffic, and ban-rate attribution stays clean per block because the binding discipline is visible in the graph rather than implicit in global settings.
RELATED