kjkuan/baguette
kjkuan/baguette has 28 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-14 22:59 UTC. Last push 2026-11-11.
What you can do
- Websocket makes it easy to write stateful and reactive web applications.
- Built-in support for OOP and modeling via Bos.
- Built-in UI widgets / components (Experimental).
- Error reporting (when set -e is used) with stack traces to help with app development.
Measured star growth
- in 24h
- 0
- in 7 days
- Collecting history
- in 15 days
- Collecting history
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
Baguette is a Bash server-side web framework for internal tooling and making reactive web forms in shell scripts.
Web & AppsREADME.md
OriginalBaguette
Baguette is a Bash server-side web framework geared towards internal tooling, or, for making reactive web forms with shell scripts. It's developed on top of htmx and uses websocketd for browser and server app communication.
It is still a work in progress, and some knowledge of htmx is required to use it effectively; however, any feedback, ideas, or contributions are welcomed. Please take a look at the issues page if you'd like to participate in its development.
NOTE: While it's possible to build a public-facing "web app" with Baguette, its main use case is for building reactive form-based UIs to existing tools / commands that are meant for internal consumptions within a secure private network.
Features
- Websocket makes it easy to write stateful and reactive web applications.
- Built-in support for OOP and modeling via Bos.
- Built-in UI widgets / components (Experimental).
- Error reporting (when
set -eis used) with stack traces to help with app development.
Limitations
A Baguette app is not RESTful; it exposes no HTTP endpoints, so you can't, for example, bookmark the state of your app via an URL, and you can't interact with it via HTTP requests (you can use CGI, but then it's a separate process).
It's slow and not scalable to high number of users. Though, depends on your use case, it might be fast enough; or, it might be good for prototyping.
Sample Baguette App
Here's what a simple, single-page app (available as example-app.sh) in Baguette looks like:
#!/usr/bin/env bash
#
set -e
source baguette.sh
declare -A STATES
@main () {
main/ id=${FUNCNAME#@} data-scope
h2/ ="Welcome to Baguette"
: ${STATES[username]:=${val_name:-}}
if [[ ! ${STATES[username]:-} ]]; then
textfield name=name label="What's your name?" placeholder="First Last" ws-send=no
button label=Submit
else
markdown "_Hello ${STATES[username]:-World}!_"
fi
local label
for label in one two three; do
checkbox =${label^} name=$label value=${label^}
done
local checked=(${val_one:-} ${val_two:-} ${val_three})
markdown "You checked: **${checked[*]}**"
/main
}
baguette
Please see Guide for a getting-started guide.
Dependencies
- websocketd (this is a fork that fixed the issue with
--passenvfor CGI scripts) - htmx (>= 2.0)
- jq
- GNU Coreutils
- Bash (>= 4.3; preferably >= 5)
Take a look at the examples to get an idea on what a Baguette app looks like. Currently, a Dockerfile is provided for building an image with the dependencies needed to run the examples.
You can build the image and run the examples following these instructions:
$ cd baguette
$ docker build -t localhost/baguette .
$ d=/home/baguette/baguette image=localhost/baguette
$ opts=(--rm -v "$PWD:${d:?}" -e WSD_PORT=5000 -p 5000:5000)
# Pick one to try: (ctrl-c to exit)
$ docker run "${opts[@]}" -w "$d/examples/todo-app" $image ./main.sh
$ docker run "${opts[@]}" -w "$d/examples/markdown-app" $image ./main.sh
$ docker run "${opts[@]}" -w "$d/examples/contact-app" $image ./main.sh
$ docker run "${opts[@]}" -w "$d/examples/wiki-app" $image ./main.sh
# NOTE: The path to /index.html is required for the URL of these apps.
Alternatively, there's now a bin/baguette-dev.sh that can be used to run any Baguette app script or folder in a docker container built from the Dockerfile.
Using Bos and the error handling outside Baguette
lib/bos.sh (the object system) and lib/errtrace.sh
(stack-trace-preserving error handling) are self-contained. Neither needs the rest
of Baguette, so any Bash script can source them. bos.sh also works on its own,
falling back to a minimal err-trace when errtrace.sh isn't loaded.
Fetch them pinned to a commit, so an upstream change can't surprise you:
REPO=kjkuan/baguette
REF=<commit-sha>
mkdir -p vendor
for f in errtrace.sh bos.sh; do
[[ -e vendor/$f ]] && continue
curl -fsSL -o vendor/$f "https://raw.githubusercontent.com/$REPO/$REF/lib/$f" || {
echo "Failed to fetch $f" >&2
exit 1
}
done
PATH=$PWD/vendor:$PATH
Then source them, errtrace.sh first:
#!/usr/bin/env bash
#
set -eEo pipefail
source errtrace.sh # must come before anything that uses err-return
trap err-trace ERR # print stack trace on non-zero command returns; goes well with set -e
source bos.sh
class Server
Server/init () {
msg $self ../init "$@"
[[ ${self[port]:-} =~ ^[0-9]+$ ]] || err-return
}
Server/url () { RESULT="http://${self[host]}:${self[port]}"; }
Server s1 host=localhost port=8080
msg $s1 url; echo "$RESULT" # -> http://localhost:8080