diff --git a/README.md b/README.md index 891bc92..398e5b7 100644 --- a/README.md +++ b/README.md @@ -15,23 +15,198 @@ Visit us on [PitchHut](https://www.pitchhut.com/project/reverse-synthid-engineer License Detection V3 Bypass - Phase Drop + V4 Bypass + Models + Attack

--- +## What the Watermark Looks Like + +SynthID encodes an imperceptible pattern directly into pixel values. On a pure **white** image generated by Gemini, the watermark is almost the entire signal. Amplify the high-frequency residual and it looks like this: + +

+ SynthID watermark pattern visible on a white Gemini-generated image +

+ +

Amplified SynthID carrier pattern extracted from a pure-white Gemini image. The diagonal banding is the watermark's spatial frequency signature — the target of our spectral attack.

+ +--- + ## Overview -This project reverse-engineers **Google's SynthID** watermarking system - the invisible watermark embedded into every image generated by Google Gemini. Using only signal processing and spectral analysis (no access to the proprietary encoder/decoder), we: +This project reverse-engineers **Google's SynthID** watermarking system — the invisible watermark embedded into every image generated by Google Gemini. Using only signal processing and spectral analysis (no access to the proprietary encoder/decoder), we: 1. **Discovered** the watermark's resolution-dependent carrier frequency structure 2. **Built a detector** that identifies SynthID watermarks with 90% accuracy 3. **Developed a multi-resolution spectral bypass** (V3) that achieves **75% carrier energy drop**, **91% phase coherence drop**, and **43+ dB PSNR** on any image resolution +4. **Generalized to multi-model, multi-color consensus** (V4) — per-model profiles for `gemini-3.1-flash-image-preview` and `nano-banana-pro-preview`, cross-color phase consensus over 6 solid backgrounds, and a human-in-the-loop calibration loop that tunes per-carrier subtraction strength from manual Gemini-app detection tallies +5. **Broke the detector across both models** (Round 06) with a unified 7-stage all-in-one attack targeting every documented SynthID failure mode simultaneously [VT-OxFF](https://github.com/VT-0xFF) built a really cool visualizer to view the process of how SynthID watermark is added to images [here](https://vt-0xff.github.io/SynthID-Explained/) (also available in repo description)! --- +## Round 06 — It Works ✓ + +After six iterative rounds of adversarial development, Round 06's `bypass_v4_final` / `bypass_v4_nuke` pipeline defeats the Gemini SynthID detector on **both** `gemini-3.1-flash-image-preview` and `nano-banana-pro-preview` images, with visually lossless output. + +### Round 01 vs Round 06 — Fidelity Comparison + +

+ Side-by-side comparison of Round 01 (gentle spectral only) vs Round 06 (all-in-one) on the same source image +

+ +

Left: Round 01 output (gentle spectral subtraction only). Right: Round 06 output (final — VAE + elastic warp + squeeze + color + JPEG). Both look identical to human eyes; only Round 06 defeats the SynthID detector.

+ +### What Changed Between Rounds + +| Round | Strategy | Outcome | +|:-----:|:---------|:-------:| +| 01 | Conservative spectral subtraction (gentle) | ✗ | +| 02 | Aggressive spectral subtraction + JPEG | ✗ | +| 03 | Blog-guided absolute bin targeting | ✗ | +| 04 | Denoise-residual phase extraction | ✗ | +| 05 | Diffusion-VAE re-generation + geometric warp | ✗ | +| **06** | **All-in-one: VAE + elastic fragmentation + squeeze + color + JPEG** | **✓** | + +The breakthrough in Round 06 came from treating the Gemini app's own published failure-mode list as an attack specification: + +> *"When an AI-generated image is part of a complex collage, layered behind other elements, or has many different textures and patterns placed over it, the detector may struggle to isolate the specific signature from the overall file."* +> — Gemini app, SynthID detection help text + +The **elastic deformation** stage simulates this effect at the pixel level: a smooth, low-frequency random warp field gives every ~50-pixel neighbourhood its own independent sub-pixel offset, fragmenting the watermark's spatial phase consensus without introducing any visible distortion. + +--- + +## V4 — Cross-Color Consensus + Human-in-the-Loop Calibration + +V4 is a ground-up re-think of the codebook built on a much richer dataset: + +- **Multi-model**: separate profiles for `gemini-3.1-flash-image-preview` and `nano-banana-pro-preview` (plus an optional `union` pseudo-model). +- **Multi-color**: 6 consensus colors (`black`, `white`, `blue`, `green`, `red`, `gray`) per model per resolution, plus `gradient` and `diverse` as content baselines. +- **Cross-color phase consensus**: the primary carrier mask. A true SynthID carrier is image-content-independent, so its phase is consistent across every solid-color background. Content-driven energy phase-scrambles across colors and drops out of the consensus. +- **Fidelity-preserving dissolver**: PSNR-floor rollback, luminance-safe DC, per-bin subtraction cap. +- **Human-in-the-loop calibration loop**: a codebook field `carrier_weights` is updated based on manual Gemini-app detection feedback. + +### Consensus coherence (why V4 wins) + +For each frequency bin `(fy, fx)` and channel `ch`: + +``` +consensus(fy, fx, ch) = | mean_over_colors( exp(i * phase_color(fy, fx, ch)) ) | +``` + +Values near `1.0` mean the phase at that bin is locked across every solid-color background, which is only true for the watermark. Content bins collapse to `< 0.3` because their phase is randomized by different color tints. On the V4 codebook built from the enriched dataset, 99%+ of content bins fall below the default `tau=0.60` cutoff, so the V4 dissolver never touches them — this is what buys back PSNR. + +### Two-phase release workflow + +```mermaid +flowchart LR + dataset[reverse-synthid-dataset
model x color x resolution] --> build[scripts/build_codebook_v4.py] + build --> codebook[artifacts/spectral_codebook_v4.npz] + codebook --> dissolve[scripts/dissolve_batch.py] + input[watermarked inputs] --> dissolve + dissolve --> variants[final / nuke variants] + variants --> gemini[Gemini app
manual SynthID detection] + gemini --> feedback[detection feedback] + feedback --> calibrate[scripts/calibrate_from_feedback.py] + calibrate -->|updates carrier_weights| codebook +``` + +### V4 Quickstart + +```bash +# 1. Build the codebook from the enriched hierarchical dataset +python scripts/build_codebook_v4.py \ + --root /path/to/reverse-synthid-dataset \ + --output artifacts/spectral_codebook_v4.npz + +# 2. Run the Round-06 all-in-one attack on a batch (recommended) +python scripts/dissolve_batch.py \ + --input ./to_clean/ \ + --output ./runs/round_06/ \ + --codebook artifacts/spectral_codebook_v4.npz \ + --model gemini-3.1-flash-image-preview \ + --strengths final nuke + +# 3. Upload each output image to the Gemini app and run SynthID detection. +# Use the results to feed back into the calibration script if needed. +``` + +### Round-06 Attack Presets + +Two presets are available via `--strengths`: + +| Preset | VAE passes | Elastic α | Squeeze | JPEG chain | PSNR floor | +|:------:|:----------:|:---------:|:-------:|:----------:|:----------:| +| `final` | 1 | 1.8 px | 90 % | q=92→88 | 14 dB | +| `nuke` | 2 | 2.8 px | 82 % | q=88→84→90 | 11 dB | + +Both presets stack the same 7-stage pipeline: + +1. **VAE round-trip** (Stable Diffusion `sd-vae-ft-mse`) — projects image off the natural-image manifold the SynthID decoder was never trained against (Gowal et al. 2026, §6.1) +2. **Elastic deformation** — smooth low-frequency random warp field, simulates the "collage fragmentation" failure mode Gemini itself acknowledges +3. **Global geometric combo** — small rotation + zoom + pixel shift in one affine warp +4. **Resize-squeeze** — downsample (AREA) → upsample (LANCZOS), erases sub-pixel watermark info +5. **Color-contrast nudge** — brightness / contrast / saturation / hue micro-shift +6. **Residual-phase FFT subtraction** — blog-universal + codebook-harvested carrier bins, cap-limited +7. **JPEG chain + luma noise + bilateral** — heavy compression / re-encoding disruption + +Every stage is independently PSNR-gated; any stage that would drop quality below the floor is rolled back automatically. + +### V4 Codebook Structure + +Profiles keyed by `(model, H, W)`. Each profile stores: + +| Field | Shape | Notes | +|------------------------|----------------|--------------------------------------------------------| +| `consensus_coherence` | `(H, W, 3)` | Primary carrier mask (cross-color phase consensus). | +| `consensus_phase` | `(H, W, 3)` | Mean unit-phase angle across colors. Subtraction template. | +| `inverted_agreement` | `(H, W, 3)` | Pairwise `abs(cos(phase_diff))`, weighted for `black<->white`. | +| `avg_wm_magnitude` | `(H, W, 3)` | Mean magnitude across consensus colors. | +| `content_baseline` | `(H, W, 3)` | From `diverse/` + `gradient/` — used for luminance blending. | +| `carrier_weights` | `(H, W, 3)` | **Live**. Starts at `consensus^2 * (0.5 + 0.5 * agreement)`. Updated by the calibration loop. | +| `n_refs_per_color` | `{color: int}` | Per-color ref counts. | + +Save format reuses the v3 compact rfft + `float16/uint8` encoding; a 14-profile codebook across 2 models × 7 resolutions is ~220 MB on disk. + +### V4 Detector (Sanity Check) + +Before spending time on manual Gemini validation, sanity-check bypass outputs against the V4 codebook's own consensus: + +```python +from robust_extractor import RobustSynthIDExtractor +from synthid_bypass_v4 import SpectralCodebookV4 + +cb = SpectralCodebookV4() +cb.load('artifacts/spectral_codebook_v4.npz') + +ext = RobustSynthIDExtractor() +result = ext.detect_from_v4_codebook(image_rgb, cb, + model='nano-banana-pro-preview') +print(result.is_watermarked, result.confidence, result.phase_match) +``` + +On the 1024x1024 exact-match path we see `conf=0.91, phase_match=0.65` for watermarked and `conf=0.02, phase_match=0.31` after aggressive V4 dissolve. + +### V4 vs V3 + +| | V3 | V4 | +|:---|:---|:---| +| Reference colors | black + white | black, white, blue, green, red, gray (+ diverse/gradient content baselines) | +| Cross-validation | `abs(cos(phase_black - phase_white))` | cross-color consensus over 6 colors + pairwise agreement | +| Models | single-model (Gemini 2.5) | per-model profiles (`gemini-3.1-flash-image-preview`, `nano-banana-pro-preview`) + optional `union` | +| Attack | spectral subtraction only | 7-stage: VAE + elastic + squeeze + color + FFT + JPEG chain | +| PSNR (aggressive) | 43 dB | visually lossless (18–24 dB pixel-level; warp displaces pixels) | +| Fidelity guard | none | per-stage PSNR-floor rollback | +| Detector bypass | local only | confirmed ✓ on Gemini app (both models) | + +V3 remains in the repo (`src/extraction/synthid_bypass.py`, `bypass_v3`) unchanged for anyone who depends on it. + +--- + ## 🚨 Contributors Wanted: Help Expand the Codebook We're actively collecting **pure black and pure white images generated by Nano Banana Pro** to improve multi-resolution watermark extraction. @@ -71,17 +246,11 @@ Dataset: [huggingface.co/datasets/aoxo/reverse-synthid](https://huggingface.co/d --- -### What Makes This Different - -Unlike brute-force approaches (JPEG compression, noise injection), our V3 bypass uses a **multi-resolution SpectralCodebook** - a collection of per-resolution watermark fingerprints stored in a single file. At bypass time, the codebook auto-selects the matching resolution profile, enabling surgical frequency-bin-level removal on any image size. - ---- - ## Key Findings ### The Watermark is Resolution-Dependent -SynthID embeds carrier frequencies at **different absolute positions** depending on image resolution. A codebook built at 1024x1024 cannot directly remove the watermark from a 1536x2816 image - the carriers are at completely different bins. +SynthID embeds carrier frequencies at **different absolute positions** depending on image resolution. A codebook built at 1024x1024 cannot directly remove the watermark from a 1536x2816 image — the carriers are at completely different bins. | Resolution | Top Carrier (fy, fx) | Coherence | Source | |:----------:|:--------------------:|:---------:|:------:| @@ -90,7 +259,7 @@ SynthID embeds carrier frequencies at **different absolute positions** depending This is why the V3 codebook stores **separate profiles per resolution** and auto-selects at bypass time. -### Phase Consistency - A Fixed Model-Level Key +### Phase Consistency — A Fixed Model-Level Key The watermark's phase template is **identical across all images** from the same Gemini model: @@ -109,28 +278,20 @@ At 1024x1024 (from black/white refs), top carriers lie on a low-frequency grid: | (10, 11) | 100.00% | 0.997 | | (13, 6) | 100.00% | 0.821 | -At 1536x2816 (from random watermarked content), carriers are at much higher frequencies: - -| Carrier (fy, fx) | Phase Coherence | -|:------------------:|:---------------:| -| (768, 704) | 99.55% | -| (672, 1056) | 97.46% | -| (480, 1408) | 96.55% | -| (384, 1408) | 95.86% | - --- ## Architecture -### Three Generations of Bypass +### Bypass Generations | Version | Approach | PSNR | Watermark Impact | Status | |:-------:|:---------|:----:|:----------------:|:------:| | **V1** | JPEG compression (Q50) | 37 dB | ~11% phase drop | Baseline | | **V2** | Multi-stage transforms (noise, color, frequency) | 27-37 dB | ~0% confidence drop | Quality trade-off | -| **V3** | **Multi-resolution spectral codebook subtraction** | **43+ dB** | **91% phase coherence drop** | **Best** | +| **V3** | **Multi-resolution spectral codebook subtraction** | **43+ dB** | **91% phase coherence drop** | Prior best | +| **V4 Round 06** | **7-stage all-in-one (VAE + elastic + squeeze + color + JPEG)** | **visually lossless** | **detector bypassed ✓** | **Current best** | -### V3 Pipeline (Multi-Resolution Spectral Bypass) +### V3 Pipeline ``` Input Image (any resolution) @@ -148,32 +309,28 @@ Input Image (any resolution) Anti-alias → Output ``` -1. **SpectralCodebook** stores resolution-specific profiles (carrier positions, magnitudes, phases) -2. **Auto resolution selection** picks the exact profile or the closest match -3. **Direct known-signal subtraction** weighted by phase consistency and cross-validation confidence -4. **Multi-pass schedule** catches residual watermark energy missed by previous passes -5. **Per-channel weighting** (G=1.0, R=0.85, B=0.70) matches SynthID's embedding strength +### V4 Round-06 Pipeline ---- - -## Results (V3 on 88 Gemini Images) - -### Aggregate Metrics (1536x2816, aggressive strength) - -| Metric | Value | -|:-------|------:| -| **PSNR** | 43.5 dB | -| **SSIM** | 0.997 | -| **Carrier energy drop** | 75.8% | -| **Phase coherence drop** (top-5 carriers) | **91.4%** | - -### Quality Across Resolutions - -| Resolution | Match | PSNR | SSIM | -|:----------:|:-----:|:----:|:----:| -| 1536x2816 | exact | 44.9 dB | 0.996 | -| 1024x1024 | exact | 39.8 dB | 0.977 | -| 768x1024 | fallback | 40.6 dB | 0.994 | +``` +Input Image (any resolution) + │ + ▼ Stage 1: VAE round-trip (SD sd-vae-ft-mse, 1-2 passes) + │ Projects image off natural-image manifold + ▼ Stage 2: Elastic deformation (smooth random warp field) + │ Fragments spatial phase consensus ("collage effect") + ▼ Stage 3: Global geometric combo (rotation + zoom + shift) + │ Single affine warp, no compounded aliasing + ▼ Stage 4: Resize-squeeze (AREA ↓ then LANCZOS ↑) + │ Erases sub-pixel watermark information + ▼ Stage 5: Color-contrast nudge (HSV micro-shift) + │ Shifts per-pixel statistics SynthID keys on + ▼ Stage 6: Residual-phase FFT subtraction + │ Blog-universal + codebook-harvested carrier bins + ▼ Stage 7: JPEG chain + luma noise + bilateral filter + │ + ▼ + Output (SynthID detector: no watermark detected ✓) +``` --- @@ -188,41 +345,32 @@ cd reverse-SynthID python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt + +# For Round-06 VAE stage: +pip install torch diffusers safetensors accelerate ``` -### 1. Build Multi-Resolution Codebook - -From the CLI: - -```bash -python src/extraction/synthid_bypass.py build-codebook \ - --black gemini_black \ - --white gemini_white \ - --watermarked gemini_random \ - --output artifacts/spectral_codebook_v3.npz -``` - -Or from Python: +### Run V4 Round-06 Bypass (Recommended) ```python -from src.extraction.synthid_bypass import SpectralCodebook +import sys +sys.path.insert(0, 'src/extraction') +from synthid_bypass_v4 import SynthIDBypassV4, SpectralCodebookV4 -codebook = SpectralCodebook() +cb = SpectralCodebookV4() +cb.load('artifacts/spectral_codebook_v4.npz') -# Profile 1: from black/white reference images (1024x1024) -codebook.extract_from_references( - black_dir='gemini_black', - white_dir='gemini_white', +b = SynthIDBypassV4() +result = b.bypass_v4_file( + 'input.png', 'output.png', + cb, + strength='final', # or 'nuke' for maximum strength + model='gemini-3.1-flash-image-preview', ) - -# Profile 2: from watermarked content images (1536x2816) -codebook.build_from_watermarked('gemini_random') - -codebook.save('artifacts/spectral_codebook_v3.npz') -# Saved with profiles: [1024x1024, 1536x2816] +print(result.stages_applied) ``` -### 2. Run V3 Bypass (Any Resolution) +### Run V3 Bypass ```python from src.extraction.synthid_bypass import SynthIDBypass, SpectralCodebook @@ -235,7 +383,6 @@ result = bypass.bypass_v3(image_rgb, codebook, strength='aggressive') print(f"PSNR: {result.psnr:.1f} dB") print(f"Profile used: {result.details['profile_resolution']}") -print(f"Exact match: {result.details['exact_match']}") ``` From the CLI: @@ -246,9 +393,7 @@ python src/extraction/synthid_bypass.py bypass input.png output.png \ --strength aggressive ``` -**Strength levels:** `gentle` (minimal, ~45 dB) > `moderate` > `aggressive` (recommended) > `maximum` - -### 3. Detect Watermark +### Detect Watermark ```bash python src/extraction/robust_extractor.py detect image.png \ @@ -264,7 +409,9 @@ reverse-SynthID/ ├── src/ │ ├── extraction/ │ │ ├── synthid_bypass.py # V1/V2/V3 bypass + multi-res SpectralCodebook -│ │ ├── robust_extractor.py # Multi-scale watermark detection +│ │ ├── synthid_bypass_v4.py # V4 cross-color consensus codebook + dissolver +│ │ ├── vae_regen.py # Round-06 SD-VAE re-generation stage +│ │ ├── robust_extractor.py # Multi-scale watermark detection (+ V4 hook) │ │ ├── watermark_remover.py # Frequency-domain watermark removal │ │ ├── benchmark_extraction.py # Benchmarking suite │ │ └── synthid_codebook_extractor.py # Legacy codebook extractor @@ -273,14 +420,27 @@ reverse-SynthID/ │ └── synthid_codebook_finder.py # Carrier frequency discovery │ ├── scripts/ -│ └── download_images.py # Download reference images from HF +│ ├── download_images.py # Download reference images from HF +│ ├── build_codebook_v4.py # V4: build per-(model, HxW) consensus codebook +│ ├── dissolve_batch.py # V4: emit strength variants +│ └── calibrate_from_feedback.py # V4: update carrier_weights from detection feedback │ ├── artifacts/ │ ├── spectral_codebook_v3.npz # Multi-res V3 codebook [1024x1024, 1536x2816] +│ ├── spectral_codebook_v4.npz # V4 codebook (per-model, per-resolution) │ ├── codebook/ # Detection codebooks (.pkl) │ └── visualizations/ # FFT, phase, carrier visualizations │ -├── assets/ # README images and early analysis artifacts +├── assets/ +│ ├── synthid_watermark.png # Watermark analysis header image +│ ├── synthid_white.jpg # Amplified SynthID pattern on white image +│ ├── v4_round1_vs_round6.png # Round 01 vs Round 06 fidelity comparison +│ └── ... +│ +├── runs/ +│ ├── round_01/ … round_05/ # Historical bypass attempts +│ └── round_06/ # Working bypass (final + nuke presets) +│ ├── watermark_investigation/ # Early-stage Nano-150k analysis (archived) └── requirements.txt ``` @@ -308,69 +468,31 @@ reverse-SynthID/ └──────────────────────────────────────────────────────────────┘ ``` -### Multi-Resolution SpectralCodebook +### Why Elastic Deformation Works -The codebook captures watermark profiles at each available resolution: - -- **1024x1024 profile**: from 100 black + 100 white pure-color Gemini outputs - - Black images: watermark is nearly the entire pixel content - - White images (inverted): confirms carriers via cross-validation - - Black/white agreement (|cos(phase_diff)|) filters out generation bias -- **1536x2816 profile**: from 88 diverse watermarked content images - - Content averages out across images; fixed watermark survives in phase coherence - - Watermark magnitude estimated as `avg_mag x coherence^2` - -### V3 Subtraction Strategy - -The bypass uses **direct known-signal subtraction** (not a Wiener filter): - -1. **Confidence** = phase_consistency x cross_validation_agreement -2. **DC exclusion** — soft ramp suppresses low-frequency generation biases -3. **Per-bin subtraction** = wm_magnitude x confidence x removal_fraction x channel_weight -4. **Safety cap** — subtraction never exceeds 90-95% of the image's energy at any bin -5. **Multi-pass** — decreasing-strength schedule (aggressive → moderate → gentle) catches residual energy +SynthID's training augmentation set (Gowal et al. 2026, Table 1) includes `SmallRotation`, `Cropresize`, `JPEG`, `GaussianBlur`, `BrightnessContrast`, and `Screenshotting` — all *global*, *uniform* spatial transforms. The elastic warp field is a *spatially varying* distortion: each local neighbourhood gets its own independent sub-pixel offset. Because the offsets are smooth (Gaussian-blurred from white noise, σ=44–56 px), the image content is visually unaffected, but the watermark's phase-consensus structure is incoherent — it can no longer be aggregated across the image. This is the pixel-level equivalent of the "collage fragmentation" effect that Gemini's own app cites as a detector failure mode. --- -## Core Modules +## Results Summary -### `synthid_bypass.py` +### V3 (spectral subtraction, 88 Gemini images) -**SpectralCodebook** — multi-resolution watermark fingerprint: +| Metric | Value | +|:-------|------:| +| **PSNR** | 43.5 dB | +| **SSIM** | 0.997 | +| **Carrier energy drop** | 75.8% | +| **Phase coherence drop** (top-5 carriers) | **91.4%** | -```python -codebook = SpectralCodebook() -codebook.extract_from_references('gemini_black', 'gemini_white') # adds 1024x1024 profile -codebook.build_from_watermarked('gemini_random') # adds 1536x2816 profile -codebook.save('codebook.npz') +### V4 Round 06 (all-in-one attack, 20 images validated) -# Later: -codebook.load('codebook.npz') -profile, res, exact = codebook.get_profile(1536, 2816) # auto-select -``` - -**SynthIDBypass** — three bypass generations: - -```python -bypass = SynthIDBypass() - -result = bypass.bypass_simple(image, jpeg_quality=50) # V1 -result = bypass.bypass_v2(image, strength='aggressive') # V2 -result = bypass.bypass_v3(image, codebook, strength='aggressive') # V3 (best) -``` - -### `robust_extractor.py` - -Multi-scale watermark detector (90% accuracy): - -```python -from robust_extractor import RobustSynthIDExtractor - -extractor = RobustSynthIDExtractor() -extractor.load_codebook('artifacts/codebook/robust_codebook.pkl') -result = extractor.detect_array(image) -print(f"Watermarked: {result.is_watermarked}, Confidence: {result.confidence:.4f}") -``` +| Model | Preset | Detector bypassed | +|:------|:------:|:-----------------:| +| gemini-3.1-flash-image-preview | `final` | ✓ | +| gemini-3.1-flash-image-preview | `nuke` | ✓ | +| nano-banana-pro-preview | `final` | ✓ | +| nano-banana-pro-preview | `nuke` | ✓ | --- @@ -378,6 +500,7 @@ print(f"Watermarked: {result.is_watermarked}, Confidence: {result.confidence:.4f - [SynthID: Identifying AI-generated images](https://deepmind.google/technologies/synthid/) - [SynthID Paper (arXiv:2510.09263)](https://arxiv.org/abs/2510.09263) +- [How to Reverse SynthID (legally😉) — Aloshdenny on Medium](https://medium.com/@aloshdenny) ---