Skip to content

Repository files navigation

Hacking Cafe

CI hakcing.cafe GitHub repo Coverage Status LICENSE AGPLv3

This project is a continuation of Fake Linux Terminal. The aim is to create a persistent Unix-like system in the browser. With the help from some backend code.

TODO

Bash

  • ~/.bashrc and /etc/bashrc files
  • $PS1 variable
    • functions in prompt (backticks)
  • $PATH variable
  • $IFS variable
  • $? variable
  • $PWD variable
  • : and . shorcuts
  • subshells
  • double-quote expressions
  • variable assignments
  • arrays
  • pipes
  • redirects (>, >>, and <)
  • JavaScript programs using web workers
  • Bash scripts
  • custom intepreters with shebang
  • variable expansions:
    • ${DATABASE_URL:="localhost"}
    • USER_ID=${1:?"Error, arg missing!"}
    • ${VAR:-default}
    • ${VAR-default}
    • ${var+alternative}
    • ${var:+alternative}
    • ${VAR/x/y} - replace
    • ${VAR//x/y} - replace all
    • ${TEXT:4} ${TEXT:0:4} - slice
    • ${FILE%.*} - remove traling non-greedy
    • ${PATH%%:*} remove traling greedy
    • ${PATH##*/} - remove greedy before
    • EXTENSION=${FILE#*.} - remove before
    • ${NAME^} - camel case
    • ${NAME^^} - upper case
    • LEN=${#NAME} - string length
    • ${!POINTER} - indirect variable
  • run in background &
  • process substitution <(command) and >(command)
  • extract commands like cat or grep into scripts in /bin
  • glob patterns
    • arrays
    • for loop
    • command arguments
    • sort the matches
    • single quotes around a pattern
    • patterns with ~ or a variable in them
    • skip dotfiles unless the pattern starts with a dot
    • */ to match directories only
  • test command
  • aliases
  • functions
    • name() { ...; } and function name { ...; }
    • positional parameters $1, $#, $*, $@ scoped to the call
    • visible in subshells, pipelines and command substitution
    • redirects on the call, f > file
    • return
    • shift
  • order: keywords => aliases => function => builtins => executables
  • printf
  • kill
  • exit
  • ps
  • type
  • if
  • &&/||
  • pushd/popd
  • while, until
  • for
  • while read line
  • case/esac
  • $0-$9
  • $@ / $* / $#
  • read -p "contunue? (y/n) "
  • history
  • export
  • unset
  • completion

FS

  • env command
  • scripts in fs as REPLs
    • /bin/js repl and interpreter
    • /bin/bash as subshell
    • /bin/php using uniter demo
    • /bin/head
    • /bin/tail
    • /bin/wc
    • /bin/cut
    • /bin/uniq
    • /bin/sort
    • /bin/tr
    • /bin/cp
    • /bin/mv
    • /bin/readlink
    • /bin/touch
    • /bin/chgrp
    • /bin/du
    • /bin/tee
    • /bin/xargs
    • /bin/basename
    • /bin/dirname
    • /bin/mktemp
    • /bin/whoami
    • /bin/who
    • /bin/date
    • /bin/uname
    • /bin/hostname
    • /bin/js
    • /bin/lolcat
    • /bin/fortune
    • /bin/python
    • /bin/sleep
    • /bin/vi (jsvi)
    • /bin/nano (micro - add to npm)

Unix

  • permissions (enforced on the server somehow)
  • login (guest without password)
  • /etc/issue
  • /etc/motd
  • /etc/group
  • CTRL+D stop the process

Custom Commands

  • fetch (neofetch)
  • rfc (RFC viewer using less)
  • less
  • record - save commands URL hash
  • credits
  • help

Tests

The Bash module is covered by unit tests that run in Node with Vitest:

npm test              # run the suite once
npm run test:watch    # re-run on change
npm run coverage      # run with a coverage report in coverage/

They run on a real LightningFS backed by memory, so file modes, symlinks and error codes behave as they do in the browser. The worker IPC is tested by standing in for the Web Worker and talking to the shell's mitty Host over the same channel the worker prelude uses — see test/helpers/. Anything that needs a real browser (the worker prelude itself, jQuery Terminal) is left to end-to-end tests.

Limitations

  • Bash expands aliases before processing keyboards, which allows changing syntax. By design Hacking Cafe will only expand aliases as commands.
  • $@ works the same as "$@" (it may change).
  • An error a JavaScript program catches from require() arrives with its name, message and the main thread's stack, but not custom properties — e.code is undefined, so match on the message to tell ENOENT from a real failure.

Commercial License

If you want to acquire a commercial license, you can contact me via jcubic@jcubic.pl.

License

    Copyright (c) 2026 Jakub T. Jankiewicz <https://jakub.jankiewicz.org>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>

About

Unix like environment in the browser (fake linux system)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages