├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── build.sh ├── container ├── bin │ └── boot.rb └── hdd │ └── .gitkeep ├── frontend ├── .gitignore ├── app.js ├── package.json └── public │ ├── 404.html │ ├── favicon.ico │ ├── fonts │ ├── perfect-dos.woff │ └── perfect-dos.woff2 │ ├── index.html │ ├── style.css │ └── tiles │ ├── tile_0.png │ ├── tile_1.png │ ├── tile_10.png │ ├── tile_11.png │ ├── tile_12.png │ ├── tile_13.png │ ├── tile_14.png │ ├── tile_15.png │ ├── tile_16.png │ ├── tile_17.png │ ├── tile_18.png │ ├── tile_19.png │ ├── tile_2.png │ ├── tile_3.png │ ├── tile_4.png │ ├── tile_5.png │ ├── tile_6.png │ ├── tile_7.png │ ├── tile_8.png │ └── tile_9.png ├── host ├── .cloudflared │ └── config.yml ├── example.service ├── install.sh ├── socket.sh └── websocketd └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | # don't accidentally commit OS images (again...) 2 | *.img 3 | *.raw 4 | *.qcow2 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/ubuntu:20.04 2 | 3 | LABEL maintainer="Jake Jarvis " 4 | LABEL repository="https://github.com/jakejarvis/y2k" 5 | LABEL homepage="https://jarv.is/y2k/" 6 | 7 | ARG DEBIAN_FRONTEND=noninteractive 8 | 9 | # corrects the time inside the Windows VM, if tzdata is installed below 10 | ENV TZ=America/New_York 11 | 12 | # do everything as an unprivileged user :) 13 | RUN useradd -m vm 14 | 15 | # copy boot script and Windows HDD (must be at ./container/hdd/hdd.img) 16 | COPY container/bin/boot.rb /usr/local/bin/boot-vm 17 | COPY --chown=vm container/hdd/hdd.img /home/vm/hdd.img 18 | 19 | RUN apt-get update \ 20 | && apt-get -y --no-install-recommends install \ 21 | tzdata \ 22 | ruby \ 23 | qemu-system-x86 \ 24 | && apt-get clean \ 25 | && rm -rf /var/lib/apt/lists/* \ 26 | && chmod +x /usr/local/bin/boot-vm \ 27 | && ls -lah /home/vm \ 28 | # make sure everything's okay so far 29 | && qemu-system-i386 --version \ 30 | && ruby --version 31 | 32 | # ---- 33 | # TODO: make *each container* a websockets server so we can load balance, etc. 34 | 35 | # ENV WEBSOCKETD_VERSION 0.3.1 36 | # RUN wget https://github.com/joewalnes/websocketd/releases/download/v${WEBSOCKETD_VERSION}/websocketd-${WEBSOCKETD_VERSION}-linux_amd64.zip \ 37 | # && unzip websocketd-${WEBSOCKETD_VERSION}-linux_amd64.zip \ 38 | # && chmod +x websocketd \ 39 | # && mv websocketd /usr/local/bin/ 40 | 41 | # RUN websocketd --version 42 | 43 | # EXPOSE 80 44 | # ---- 45 | 46 | # bye bye root <3 47 | USER vm 48 | WORKDIR /home/vm 49 | 50 | ENTRYPOINT ["boot-vm"] 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present Jake Jarvis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💾 [Y2K Sandbox](https://jarv.is/y2k/) [![Uptime Robot status](https://img.shields.io/uptimerobot/status/m785127956-49458d510e68142930db872d?logo=windows%2095)](https://jarv.is/y2k/) [![Uptime Robot ratio (7 days)](https://img.shields.io/uptimerobot/ratio/7/m785127956-49458d510e68142930db872d?color=%23638ebd)](https://status.pipe.fail/check/597930) 2 | 3 | Nostalgic time machine powered by on-demand Windows Me® VMs, [my first website](https://github.com/jakejarvis/my-first-website), and quarantine boredom. 📟 4 | 5 | [**📝 Read the blog post here.**](https://jarv.is/notes/y2k-sandbox/) 6 | 7 |

8 | 9 | ## Requirements 10 | 11 | - [Docker](https://www.docker.com/) 12 | - [QEMU](https://www.qemu.org/) 13 | - [websocketd](https://github.com/joewalnes/websocketd) 14 | - [noVNC](https://github.com/novnc/noVNC) 15 | - [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/) (backend) and [Pages](https://pages.cloudflare.com/) (frontend) 16 | - [Microsoft Bob](https://en.wikipedia.org/wiki/Microsoft_Bob) 17 | 18 | ## Inspired By 19 | 20 | - [charlie.bz](https://charlie.bz/) 21 | - [benjojo.co.uk](https://benjojo.co.uk/) 22 | - [Microsoft Bob](https://en.wikipedia.org/wiki/Microsoft_Bob) 23 | 24 | ## To-Do 25 | 26 | - [x] **Commit backend scripts** 27 | - [x] Sync user's mouse cursor/movements with VM 28 | - [x] Error messages: no websockets, server down, etc. 29 | - [ ] Usage limits 30 | - [ ] Responsive browser sizing 31 | 32 | ## License 33 | 34 | MIT 35 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | # what a mess. https://stackoverflow.com/a/53183593 6 | YOU_ARE_HERE="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" 7 | 8 | # container will be useless unless we bundle the actual OS 9 | test -f "$YOU_ARE_HERE"/container/hdd/hdd.img 10 | 11 | # build the container & tag it locally 12 | docker build -t git.pipe.fail/jake/y2k:latest --squash --no-cache "$YOU_ARE_HERE" 13 | docker push git.pipe.fail/jake/y2k:latest 14 | -------------------------------------------------------------------------------- /container/bin/boot.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: BINARY 3 | # warn_indent: true 4 | # frozen_string_literal: true 5 | 6 | # This script starts a QEMU child process wearing a VNC sock and acts as 7 | # middleman between the socket and stdin/out. Perfect for VNC clients that 8 | # utilize binary websockets (ex: noVNC.js). 9 | # 10 | # Usage: ./boot.rb /root/images [/usr/local/bin/qemu-system-i386] 11 | 12 | require "fileutils" 13 | require "socket" 14 | require "timeout" 15 | 16 | # folder containing the OS's hdd.img, other instance files will also go here 17 | # default for container is set, can be optionally overridden by first argument 18 | base_path = ARGV[0] || "/home/vm" 19 | 20 | # location of QEMU binary (`qemu-system-i386` here, or `-x86_64` for 64-bit) 21 | # default for container is set, can be optionally overridden by second argument 22 | qemu_path = ARGV[1] || "/usr/bin/qemu-system-i386" 23 | 24 | # create a temporary directory for each instance from PID 25 | # NOTE: not needed when containerized, everything's already ephemeral 26 | # instance_dir = "/tmp/y2k.#{$$}" 27 | 28 | # flush data immediately to stdout instead of buffering 29 | # https://ruby-doc.org/core-2.7.0/IO.html#method-i-sync-3D 30 | $stdout.sync = true 31 | 32 | begin 33 | # make the temp dir for our new instance & grab a fresh copy of the OS 34 | # NOTE: not needed when containerized, everything's already ephemeral 35 | # FileUtils.makedirs(instance_dir) 36 | # FileUtils.cp(base_img, "#{instance_dir}/hdd.img") 37 | 38 | # open a catch-all log file 39 | log_file = File.open("#{base_path}/out.log", "w") 40 | 41 | # start QEMU as a child process (TODO: put config somewhere more manageable) 42 | qemu = spawn qemu_path, 43 | "-drive", "file=#{base_path}/hdd.img,format=qcow2", 44 | "-cpu", "pentium3,enforce", 45 | "-m", "96", 46 | "-net", "none", 47 | "-serial", "none", 48 | "-parallel", "none", 49 | "-vga", "std", 50 | "-usb", 51 | "-device", "usb-tablet", 52 | "-rtc", "base=localtime", 53 | "-no-acpi", 54 | "-no-reboot", 55 | "-nographic", 56 | "-vnc", "unix:#{base_path}/vnc.sock", 57 | { :in => :close, :out => log_file, :err => log_file } 58 | 59 | # limit CPU usage of each VM (if host supports it) 60 | # NOTE: setting --cpus with Docker makes this redundant 61 | # if File.exist?("/usr/bin/cpulimit") 62 | # cpulimit = spawn "/usr/bin/cpulimit", 63 | # "--pid", "#{qemu}", 64 | # "--limit", "90", 65 | # { :in => :close, :out => log_file, :err => log_file } 66 | # end 67 | 68 | # wait until the VNC socket is created; only takes a fraction of a second (if 69 | # the server load is low) but everything following this will freak the f*ck 70 | # out if it's not there yet 71 | Timeout.timeout(15) do 72 | until File.exist?("#{base_path}/vnc.sock") 73 | sleep 0.02 74 | end 75 | end 76 | 77 | # attach ourselves to the VM's VNC socket made by QEMU 78 | sock = UNIXSocket.new("#{base_path}/vnc.sock") 79 | 80 | # everything's all set up now, time to simply pass data between user and VM 81 | while $stdin do 82 | begin 83 | # monitor the IO buffer for unprocessed data (read from both directions) 84 | read, _, err = IO.select([$stdin, sock], nil) 85 | 86 | # break out of loop if anything goes wrong, doesn't really matter what tbh 87 | if err.any? 88 | break 89 | end 90 | 91 | # pass input from user to VM 92 | if read.include?($stdin) 93 | data = $stdin.readpartial(4096) 94 | sock.write(data) unless data.empty? 95 | end 96 | 97 | # pass output from VM to user 98 | if read.include?(sock) 99 | data = sock.readpartial(4096) 100 | $stdout.write(data) unless data.empty? 101 | 102 | # send output immediately (see $stdout.sync above) 103 | $stdout.flush 104 | end 105 | rescue EOFError 106 | # we stopped receiving input from the user's end, so don't expect any more 107 | break 108 | end 109 | end 110 | ensure 111 | # the user's done (or we crashed) so stop their personal VM; everything else 112 | # will be deleted along with the Docker container 113 | Process.kill(:SIGTERM, qemu) if qemu 114 | 115 | # kill cpulimit if it didn't stop itself already 116 | # Process.kill(:SIGTERM, cpulimit) if cpulimit 117 | 118 | # ...and delete their hard drive, logs, etc. 119 | # NOTE: not needed when containerized 120 | # FileUtils.rm_rf(instance_dir) 121 | end 122 | -------------------------------------------------------------------------------- /container/hdd/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/container/hdd/.gitkeep -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | public/app.js 2 | public/app.js.map 3 | node_modules/ 4 | yarn.lock 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /frontend/app.js: -------------------------------------------------------------------------------- 1 | import RFB from "@novnc/novnc/core/rfb.js"; 2 | 3 | // DOS-style box for text 4 | const cmd = document.getElementById("cmd-text"); 5 | 6 | if (window.WebSocket) { 7 | // https://github.com/novnc/noVNC/blob/master/docs/API.md 8 | const rfb = new RFB( 9 | document.getElementById("display"), 10 | "wss://y2k.pipe.fail", 11 | { 12 | wsProtocols: ["binary", "base64"] 13 | } 14 | ); 15 | rfb.addEventListener("connect", () => { 16 | console.log("successfully connected to VM socket!"); 17 | }); 18 | rfb.addEventListener("disconnect", (detail) => { 19 | console.warn("VM ended session remotely:", detail); 20 | }); 21 | 22 | // give up after 15 seconds (this also is rendered behind the VNC display in case it crashes and goes poof) 23 | setTimeout(() => { 24 | cmd.textContent = "Oh dear, it looks like something went very wrong. :(\n\nRefresh or try again in a bit.\n\n\nPress the Any key to continue."; 25 | }, 15000); 26 | } else { 27 | // browser doesn't support websockets 28 | cmd.textContent = "WebSockets must be enabled to play in the Y2K Sandbox!!!\n\nPress the Any key to continue."; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "esbuild app.js --outdir=public --platform=browser --target=es2020 --bundle --minify --sourcemap" 4 | }, 5 | "dependencies": { 6 | "@novnc/novnc": "1.3.0" 7 | }, 8 | "devDependencies": { 9 | "@types/novnc__novnc": "^1.3.0", 10 | "esbuild": "^0.13.14" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found 6 | 63 | 64 | 65 |
66 |

Windows

67 |

A fatal exception 404_NOT_FOUND has occured at 0x69ABC420. The current application will be terminated.

68 |
    69 |
  • Press any key to terminate the current application.
  • 70 |
  • Press CTRL+ALT+DEL to restart your computer. If you do this, you will lose any unsaved information in all open applications.
  • 71 |
72 |

  Click here to return to Windows _

73 |
74 | 75 | 76 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/fonts/perfect-dos.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/fonts/perfect-dos.woff -------------------------------------------------------------------------------- /frontend/public/fonts/perfect-dos.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/fonts/perfect-dos.woff2 -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | Y2K Sandbox — Powered by Windows Me™ 💾 13 | 14 | 15 | 16 | 17 | 18 |
JavaScript is required for this experience.

Please enable it and refresh the page!
_
19 |
20 | 21 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /frontend/public/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Perfect DOS VGA 437"; 3 | font-style: normal; 4 | font-weight: normal; 5 | src: url("fonts/perfect-dos.woff2") format("woff2"), 6 | url("fonts/perfect-dos.woff") format("woff"); 7 | } 8 | 9 | body { 10 | font-family: "Perfect DOS VGA 437", monospace; 11 | width: 100%; 12 | height: 100%; 13 | margin: 0; 14 | padding: 0; 15 | user-select: none; 16 | 17 | /* specific retro wallpaper tile is set randomly by JS onload */ 18 | background-color: #000; 19 | background-repeat: repeat; 20 | background-attachment: fixed; 21 | background-position: center; 22 | } 23 | 24 | a { 25 | color: inherit; 26 | text-decoration: underline; 27 | } 28 | 29 | div#cmd, div#display { 30 | display: block; 31 | margin: auto; 32 | position: absolute; 33 | top: 0; 34 | bottom: 0; 35 | left: 0; 36 | right: 0; 37 | } 38 | 39 | div#cmd { 40 | width: 600px; 41 | height: 300px; 42 | padding: 12px; 43 | background-color: #000; 44 | z-index: -100; 45 | } 46 | div#cmd span { 47 | color: #ccc; 48 | white-space: pre; 49 | } 50 | 51 | div#display { 52 | max-width: 800px; 53 | max-height: 600px; 54 | 55 | z-index: 100; 56 | 57 | /* fix fuzziness: https://stackoverflow.com/a/13492784 */ 58 | image-rendering: optimizeSpeed; 59 | image-rendering: -moz-crisp-edges; 60 | image-rendering: -o-crisp-edges; 61 | image-rendering: -webkit-optimize-contrast; 62 | image-rendering: crisp-edges; 63 | image-rendering: pixelated; 64 | -ms-interpolation-mode: nearest-neighbor; 65 | } 66 | div#display div { 67 | background: none !important; 68 | } 69 | div#display div canvas { 70 | cursor: default !important; 71 | } 72 | 73 | span.blink { 74 | animation: blink 1s step-end infinite; 75 | } 76 | @keyframes blink { 77 | 50% { 78 | opacity: 0; 79 | } 80 | } 81 | 82 | /* http://tholman.com/github-corners/ */ 83 | a#github-corner svg { 84 | fill: #fff; 85 | color: #333; 86 | position: absolute; 87 | top: 0; 88 | border: 0; 89 | right: 0; 90 | } 91 | a#github-corner svg path.octo-arm { 92 | transform-origin: 130px 106px; 93 | } 94 | a#github-corner:hover svg path.octo-arm { 95 | animation: octocat-wave 560ms ease-in-out; 96 | } 97 | @keyframes octocat-wave { 98 | 0%, 100% { 99 | transform: rotate(0); 100 | } 101 | 20%, 60% { 102 | transform: rotate(-25deg); 103 | } 104 | 40%, 80% { 105 | transform: rotate(10deg); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /frontend/public/tiles/tile_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_0.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_1.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_10.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_11.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_12.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_13.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_14.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_15.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_16.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_17.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_18.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_19.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_2.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_3.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_4.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_5.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_6.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_7.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_8.png -------------------------------------------------------------------------------- /frontend/public/tiles/tile_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/frontend/public/tiles/tile_9.png -------------------------------------------------------------------------------- /host/.cloudflared/config.yml: -------------------------------------------------------------------------------- 1 | hostname: y2k.pipe.fail 2 | url: http://localhost:80 3 | logfile: /var/log/cloudflared.log 4 | tunnel: 5 | credentials-file: 6 | -------------------------------------------------------------------------------- /host/example.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WebSockets server 3 | After=network.target 4 | StartLimitIntervalSec=0 5 | 6 | [Service] 7 | Type=simple 8 | Restart=always 9 | RestartSec=1 10 | User=root 11 | ExecStart=/root/y2k/host/socket.sh 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /host/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # WARNING: you probably shouldn't just run this! ;) 4 | 5 | set -euxo pipefail 6 | 7 | REPO_DIR=/root/y2k 8 | 9 | #### install basic requirements #### 10 | apt-get -y update 11 | apt-get -y dist-upgrade 12 | apt-get -y --no-install-recommends install \ 13 | apt-transport-https \ 14 | ca-certificates \ 15 | gnupg-agent \ 16 | software-properties-common \ 17 | git \ 18 | unzip 19 | 20 | #### clone repository for scripts #### 21 | git clone https://github.com/jakejarvis/y2k.git $REPO_DIR 22 | 23 | #### install Docker from official repository #### 24 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - 25 | apt-key fingerprint 0EBFCD88 26 | add-apt-repository \ 27 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ 28 | $(lsb_release -cs) \ 29 | stable" 30 | apt-get update 31 | apt-get -y install \ 32 | docker-ce \ 33 | docker-ce-cli \ 34 | containerd.io 35 | docker version 36 | 37 | #### docker fixes #### 38 | ## https://github.com/moby/moby/issues/4250 39 | sed -i 's/\(GRUB_CMDLINE_LINUX="\)"/\1cgroup_enable=memory swapaccount=1"/' /etc/default/grub 40 | update-grub 41 | ## enable `docker build --squash` 42 | echo "{ \"experimental\": true }" > /etc/docker/daemon.json 43 | 44 | #### install websocketd #### 45 | ## https://github.com/joewalnes/websocketd/releases 46 | WEBSOCKETD_VERSION=0.4.1 47 | wget -nv -P /tmp/ https://github.com/joewalnes/websocketd/releases/download/v${WEBSOCKETD_VERSION}/websocketd-${WEBSOCKETD_VERSION}-linux_amd64.zip 48 | unzip /tmp/websocketd-${WEBSOCKETD_VERSION}-linux_amd64.zip websocketd -d /tmp 49 | mv /tmp/websocketd /usr/local/bin/ 50 | chmod +x /usr/local/bin/websocketd 51 | rm /tmp/websocketd-${WEBSOCKETD_VERSION}-linux_amd64.zip 52 | websocketd --version 53 | 54 | #### install cloudflared #### 55 | ## https://developers.cloudflare.com/argo-tunnel/downloads/ 56 | wget -nv -P /tmp/ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb 57 | dpkg -i /tmp/cloudflared-linux-amd64.deb 58 | rm /tmp/cloudflared-linux-amd64.deb 59 | cloudflared version 60 | 61 | #### login to cloudflare #### 62 | cp $REPO_DIR/host/.cloudflared/config.yml /etc/cloudflared/ 63 | cloudflared tunnel login 64 | cloudflared service install 65 | # move auto-downloaded certificate to a more sensible location 66 | cp ~/.cloudflared/cert.pem /etc/cloudflared/ 67 | rm ~/.cloudflared/cert.pem 68 | 69 | #### login to registry & pull existing OS container #### 70 | docker login git.pipe.fail 71 | docker pull git.pipe.fail/jake/y2k:latest 72 | 73 | #### enable services #### 74 | cp $REPO_DIR/host/example.service /lib/systemd/system/y2k.service 75 | systemctl daemon-reload 76 | systemctl enable y2k 77 | systemctl enable cloudflared 78 | 79 | #### reboot #### 80 | echo "Rebooting shortly..." 81 | sleep 15 82 | reboot 0 83 | -------------------------------------------------------------------------------- /host/socket.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | REPO_DIR=/root/y2k 4 | IMAGE_NAME=git.pipe.fail/jake/y2k:latest 5 | 6 | $REPO_DIR/host/websocketd \ 7 | --port=80 \ 8 | --binary \ 9 | --header-ws="Sec-WebSocket-Protocol: binary" \ 10 | --origin=jarv.is,www.jarv.is,y2k.pages.dev \ 11 | -- \ 12 | docker run \ 13 | --cpus 1 \ 14 | --memory 128m \ 15 | --network none \ 16 | --log-driver none \ 17 | --rm -i \ 18 | $IMAGE_NAME 19 | 20 | # to spawn QEMU processes natively on the host machine instead of via 21 | # individual Docker containers: 22 | # /root/y2k/container/bin/boot.rb /root/y2k/container/hdd /usr/bin/qemu-system-i386 23 | -------------------------------------------------------------------------------- /host/websocketd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/host/websocketd -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakejarvis/y2k/f632da619576010e81d484538dfdea4fd2fcd6fe/screenshot.png --------------------------------------------------------------------------------