Protocol experiment · 001

Pushing Git until it plays Bad Apple

What happens when a Git server treats terminal output as a canvas?

Ben Swerdlow · July 24, 2026

git clone https://git.swerdlow.dev/bad-apple
                  ..::--==++**##%%%%##**++==--::..
             .-=*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*=-.
          .=%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%=.
        :#@@@@@@@@@@@@@@%*=-:......:-=*%@@@@@@@@@@@@@@#:
       #@@@@@@@@@@@@@%=.                .=%@@@@@@@@@@@@@#
      @@@@@@@@@@@@@*.       .:----:.       .*@@@@@@@@@@@@@
     @@@@@@@@@@@@@:       :%@@@@@@@@%:       :@@@@@@@@@@@@@
     @@@@@@@@@@@@+       +@@@@@@@@@@@@+       +@@@@@@@@@@@@
     @@@@@@@@@@@@.       @@@@%*++*%@@@@       .@@@@@@@@@@@@
     @@@@@@@@@@@@+       *@@@.    .@@@*       +@@@@@@@@@@@@
      @@@@@@@@@@@@*.       --      --       .*@@@@@@@@@@@@
       #@@@@@@@@@@@@%=.                .=%@@@@@@@@@@@@@#
        :#@@@@@@@@@@@@@@%*=-:....:-=*%@@@@@@@@@@@@@@#:
          .=%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%=.
             .-=*%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%*=-.
Server-defined progress, rendered as frames.

Git servers have always sent messages to clients: counting objects, compressing deltas, reporting progress. Those messages are still bytes headed for a terminal, and terminals can do much more than print a line of text.

I built a small smart-HTTP server that uses progress messages to draw frames with cursor movement, color, and block characters. The first target was Bad Apple, the inevitable test for any display that can technically move pixels.

From there came a holographic spiral, a color ASCII Rickroll, and a fluid field with terminal hyperlinks embedded into the water.

Try the endpoints

       .-=*#%%%#*=-.
    .=%@@@@@@@@@@@@%=.
   *@@@@@%*-:-*%@@@@@*
  %@@@@*.      .*@@@@%
  @@@@+   :--:   +@@@@
  @@@@.  *@@@@*  .@@@@
  %@@@@*.  ..  .*@@@@%
   *@@@@@%*==*%@@@@@*
    .=%@@@@@@@@@@%=.
       .-=****=-.
Bad Apple
  ████████████████
 ████          ████
███  ████████    ███
██  ███    ████   ██
██  ██  ██   ███  ██
██  ███  ███  ██  ██
██   ████    ███  ██
 ███    ██████  ███
  ████        ████
    ████████████
Spiral
 · ≈≈ ░░░░ ≈ ·   ≈≈
≈ ░░▒▒▒▒░░ ≈≈ ░░░
░▒▒▓▓▓▒░ ≈  ><(((°>
≈░▒▓█▓▒░ ≈≈ ░▒▒░ ≈
  ≈░▒░ ≈ · ≈░▓░≈
·   ≈  ·   o   ≈
  ·     o      ·
Fluid
  • Bad Apple git clone https://git.swerdlow.dev/bad-apple
  • Rickroll git clone https://git.swerdlow.dev/ricky
  • Spiral git clone https://git.swerdlow.dev/spiral
  • Fluid git clone https://git.swerdlow.dev/fluid
  • LFS finale git clone https://git.swerdlow.dev/lfs-demo

Two display paths

Sideband
Channel 2 carries server-defined progress. The finite demos send frames there, then switch to a real pack on channel 1.
Checkout
Bad Apple and Ricky finish as valid repositories instead of ending with an early EOF.
Git LFS
A repository-selected LFS endpoint supplies the final terminal banner through a separate checkout-time diagnostic path.

This is terminal control, not arbitrary code execution. Current upstream Git sanitizes sideband controls by default; Apple Git 2.50.1 passes the controls used here. The finale requires Git LFS, and terminal policy still decides which sequences take effect.