A zsh-compatible shell with BrightDate woven natively into its builtins, parameters, prompt escapes, and file timestamps. One sortable, timezone-free scalar — everywhere.
Shell scripts that touch time pay a hidden tax: parsing
date output, juggling $TZ, sub-shelling out
to gdate on macOS, fighting epoch formats that differ
between stat on Linux vs BSD, and re-implementing
duration math by hand. BSH replaces all of that with a single decimal
scalar — a timezone-free Float64 count of SI days since J2000.0 (the
same epoch used by every space agency and observatory).
BSH is the living proof: every time-related surface in the
shell — prompt, ls -l, stat,
history -d, sched, times —
emits the same float. No translation. No format mismatch. Duration
arithmetic in $(( )) just works.
The BSH chsh challenge:
switch your login shell for a week. Your prompt shows the BrightDate.
Your files are stamped in BrightDate. Your history is indexed in
BrightDate. Duration arithmetic happens in your head. Once you stop
translating, it's hard to go back — that's the point.
BrightDate is a
Float64 count of SI days since the astronomical epoch
J2000.0,
computed on a TAI substrate. Trivially sortable, diffable, and
storable — b − a = elapsed days, no exceptions.
| Problem | BrightDate fix |
|---|---|
| Timezone soup | Single universal value, no zones |
| Leap-second stutters | TAI substrate — monotonic, no jumps |
| Duration arithmetic | b - a in shell $(( … )) |
| Sorting timestamps |
Float compare; works in sort -n,
awk, jq
|
| 2038 / Y10K | ±287,000 years at sub-µs in current era |
date portability |
Identical output on macOS, Linux, BSD, WSL |
BSH = zsh 5.10 + a statically-linked Rust core (brightdate-rust) wired into the shell's C internals through a single FFI entry
point:
double bsh_unix_to_brightdate(double unix_secs);
double bsh_brightdate_to_unix(double bd);
From those two anchors, every time-related surface — builtins,
parameters, prompt escapes, history, file timestamps — speaks
BrightDate. Nothing parses strings. Nothing re-derives the epoch.
The number that comes out of $BRIGHTEPOCH is the same
number that stat prints for mtime, the
same number in your prompt, and the same number you can subtract
in an arithmetic expansion.
BSH stays zsh-compatible: every traditional builtin, option, and module still works. BrightDate is layered on top — see how BSH differs from zsh.
BSH is a fork of
zsh 5.10, not a
new shell language. Your zsh scripts, completion functions, options,
and module layout largely carry over unchanged — including
setopt, zmodload, compinit,
and the full zsh grammar. BSH adds a BrightDate-native time layer on
top and a handful of BSH-only modules; it does not remove upstream
behaviour.
| Surface | zsh | BSH |
|---|---|---|
| Time scalar | Unix epoch ($EPOCHSECONDS, $EPOCHREALTIME) |
BrightDate
— SI days since J2000.0 ($BRIGHTEPOCH)
|
date |
External command; locale and OS dependent |
Shell builtin (bdate); BrightDate by default, with
ISO/JD/GPS/strftime conversions
|
time / btime |
Reserved word; TIMEFMT reports seconds
(%E, %U, %S)
|
Same keyword plus btime builtin; default
TIMEFMT uses millidays (%dE,
%dU, %dS). GNU
time -f support with BrightDate extensions
(%B, %b, %N,
%n, %Wt, %Ws)
|
| Prompt clock | %T, %*… strftime escapes |
%P — current BrightDate (6 d.p.) |
history -d |
Calendar date/time strings | BrightDate command timestamps |
fc -l -D |
Duration in seconds | Duration in millidays |
ls -l |
Locale-dependent mtime column |
BrightDate decimal (zmodload bsh/files); flags
-u/-c select atime/ctime
|
stat |
Unix seconds or strftime via -F |
BrightDate defaults; new btime birth field;
-r / -r -s for Unix side-by-side
|
sched |
HH:MM:SS offsets only |
Also BrightDate floats: relative +0.020833 or
absolute 9628.5
|
times |
mm:ss user/system lines |
CPU time in millidays |
uptime / cal / watch |
External, calendar/clock oriented |
Builtins reporting millidays and BrightDate clocks
(buptime, bcal, bwatch)
|
Glob .m / .a / .c |
Integer day/hour/minute counts |
Fractional decimal-day values (centiday
precision); new .b birth-time qualifier
|
| Credential delivery | — |
BrightLink
(bsh-inject, link-geo) — no zsh
equivalent
|
bdate, btime, and friends
The five classic Unix time commands are reimplemented in Rust as
brightdate-rust
(currently v0.5.7). Inside BSH they register as shell
builtins and shadow date, time,
uptime, cal, and watch. Outside
BSH the same binaries install as bdate,
btime, buptime, bcal, and
bwatch — prefixed so they do not fight the system
originals on PATH.
bsh -c date and
bdate call the same Rust entry point. Differences vs zsh
are therefore identical whether you use the builtin or the standalone
CLI — only the invocation name and whether a subprocess is forked
change.
| Tool | BSH builtin | Replaces | What changes vs the traditional tool |
|---|---|---|---|
bdate |
date |
date(1) |
Default output is a BrightDate scalar, not a locale string.
Accepts BrightDate, ISO 8601, JD:,
MJD:, or Unix ms input. Emits ISO/RFC/Julian/GPS,
strftime patterns, TAI, breakdown, and day diffs via flags.
|
btime |
time / btime |
/usr/bin/time |
Default report is a multi-line BrightDate timing summary (days,
millidays, start/end BD, CPU %). Optional colored stderr.
Full GNU time 1.10 -f/-p/-v
modes plus BrightDate % extensions including
%Wt/%Ws.
|
buptime |
uptime |
uptime(1) |
Uptime shown in millidays; boot time as a BrightDate decimal alongside the familiar load-average line. |
bcal |
cal |
cal(1) |
Standard month/year grid plus a BrightDate annotation for each
day (noon UTC). Supports -3, -y, color,
and configurable precision.
|
bwatch |
watch |
watch(1) |
Re-runs a command on an interval; header interval shown in
millidays. Accepts -n seconds and -c
iteration count.
|
bdate / date
Parse almost anything, emit BrightDate by default. Calendar formats
honor --local; astronomical formats include Julian,
modified Julian, and GPS week.
$ bdate
9628.195697
$ bdate -f iso
2000-01-01T12:00:00Z
$ bdate -d 9628.5
0.304303
buptime / uptimeFamiliar load-average line, but uptime in millidays and boot time as a BrightDate decimal.
$ buptime
up 24 days, 14:59
(24624 md) — boot 9603.57128
bcal / cal
Standard month/year grid with a BrightDate annotation per day
(noon UTC). Supports -3, -y, and color.
$ bcal -3
May 2026
…
1: 9628.50
bwatch / watch
Re-runs a command on an interval; header interval shown in
millidays. Accepts -n seconds and -c
iteration count.
$ bwatch -n 2 date
Every 0.023 md: …
bsh -c date and bdate call the same Rust
entry point. Differences vs zsh are identical whether you use the
builtin or the standalone CLI — only the invocation name and
whether a subprocess is forked change.
btime / time
Three output modes: (1) BrightDate report (default,
colorized on TTY
via --color=auto), (2) GNU -f format
strings, (3) POSIX -p or verbose -v.
As a BSH builtin, bad flags return an error code instead of killing
the shell.
btime format specifiers (GNU + BrightDate)
When -f, -p, or -v is selected,
btime follows GNU time 1.10 for standard letters
(%E, %U, %S, %P,
%M, …) and adds BrightDate extensions (see
FORMAT-SPEC.md):
| Specifier | Meaning |
|---|---|
%dE / %dU / %dS |
Elapsed / user / system time in millidays (%.6f md) —
same shape as BSH default TIMEFMT
|
%B |
Elapsed wall time in BrightDate days (9 d.p.) |
%b |
Elapsed in millidays (6 d.p., no suffix — machine form of %dE) |
%N / %n |
Start / end BrightDate wall time (9 d.p.) |
%Wt / %Ws |
End / start BrightDate (9 d.p.) — same format as
bfind -printf '%Wt'
|
%W |
GNU swap count (unchanged; use %Wt for BrightDate) |
Color flags: --color=auto|always|never|ansi|truecolor,
--no-color, --color-scheme=default|bright,
plus BTIME_COLOR, BTIME_COLOR_SCHEME,
NO_COLOR. GNU modes disable color automatically. On a TTY,
only the default BrightDate report is colorized — not -f
output; labels are cyan/green/yellow/blue by field — same scanning style as
bfind
and
bright-iputils.
Full cross-tool rules:
FORMAT-SPEC.md.
Install standalone tools:
brew install digital-defiance/tap/bdate btime buptime bcal bwatch
or cargo install bdate btime buptime bcal bwatch. Inside
BSH they are already present as builtins — no separate install required.
BSH preserves the zsh feature set you already rely on: extended globbing, parameter expansion, job control, the line editor (ZLE), completion system, hash directories, multibyte/UTF-8 support, and the module ecosystem. Third-party frameworks such as Oh My Bsh target BSH directly but inherit zsh conventions.
Environment and startup files follow the zsh layout
(/etc/bsh, ~/.bshrc, etc.) with BSH
naming. Parameters like $ZSH_VERSION are mirrored by
$BSH_VERSION and $BSH_PATCHLEVEL.
BrightDate conversion lives in a statically-linked Rust library
(brightdate-rust) exposed through two C FFI entry points. Shell builtins
(date, time, …) call into that library
instead of fork/execing external tools or reimplementing calendar
math in C.
double bsh_unix_to_brightdate(double unix_secs);
double bsh_brightdate_to_unix(double bd);
The same crate powers standalone CLI tools
(bdate, btime, …) for use outside the
shell, and the BrightDate ecosystem on npm and crates.io.
The
bright-findutils
toolchain (bfind, blocate, …) prints file
timestamps with -printf '%Wt' — modification time as a
BrightDate decimal with 9 places, matching the C library in
brightdate-rust. BSH’s btime builtin uses
the same %Wt / %Ws letters in GNU
-f format strings, but for command timing:
| Specifier | bfind -printf |
btime -f |
|---|---|---|
%Wt |
File mtime (BrightDate) | Command end time (BrightDate) |
%Wa / %Wc / %WB |
atime / ctime / birth time | — (not applicable to timing) |
%Ws |
— | Command start time (BrightDate) |
$ bfind . -name '*.log' -printf '%Wt %p\n'
9628.195697 ./app.log
$ btime -f '%Wt %C' -- sleep 0.1
9628.195712 sleep
$ btime -f 'start=%Ws end=%Wt elapsed=%B' -- ./deploy.sh
start=9628.195700 end=9628.195850 elapsed=0.000150
Because both tools emit the same scalar, log lines from
bfind, timing output from btime, and your
shell prompt (%P) can be compared, sorted, and subtracted
without conversion — paste a %Wt from a build log into
sched or compare it against $BRIGHTEPOCH in
arithmetic expansion.
TIMEFMT (the time keyword)
When you prefix a command with the time reserved word, zsh
formats resource usage using $TIMEFMT. BSH changes the
default template to millidays:
# zsh default (seconds)
TIMEFMT='%J %U user %S system %P cpu %*E total'
# BSH default (millidays)
TIMEFMT='%J %dU user %dS sys %P cpu %dE real'
$ time sleep 1
0.011574 md user 0.004630 md sys 0.2% cpu 0.011574 md real
# Override back to zsh-style seconds anytime:
TIMEFMT='%J %U user %S system %P cpu %E real'
The standalone btime builtin (also invoked as
time for timing commands) prints a richer BrightDate
report by default — elapsed days, millidays, microdays, nanodays,
start/end BrightDate, and CPU percentage — and accepts GNU
-f/-p/-v when you need
traditional /usr/bin/time output instead.
BSH bakes BrightDate directly into the classic Unix date/time
commands. date, time, uptime,
cal, and watch are shell builtins backed by
the same Rust crates as the standalone
bdate/btime/… CLI tools — no fork, no
external process, no $TZ dance. The traditional binaries
are shadowed transparently, so existing scripts switch over for free.
btime colorizes its default report on TTYs for easy
scanning. See
full tool-by-tool differences.
datebdate — -f bright/iso/julian/gps/all,
--strftime, --local,
--precision, --breakdown,
--tai, --diff; input as BrightDate, ISO,
JD:, MJD:, or Unix ms
timebtime — BrightDate elapsed report (colored on TTY) or
GNU -f/-p/-v/-o;
%Wt/%Ws/%B/%N
format extensions. Use btime --help — not
time --help (reserved word)
uptimebuptime — load averages plus uptime in millidays and
boot BrightDate
calbcal — month/year grid with per-day BrightDate
annotations; -3, -y,
--no-color, --precision
watchbwatch — periodic re-run; interval header in
millidays; -n seconds, -c count
$BRIGHTEPOCH
From bsh/datetime. Read-only Float64 parameter
holding the current BrightDate, updated on every access — the
analogue of $EPOCHREALTIME. Perfect for elapsed-time
arithmetic in scripts.
start=$BRIGHTEPOCH
do_work
echo "elapsed: $(( BRIGHTEPOCH - start )) md"
%P
In any PROMPT, RPROMPT, or
PS4, %P expands to the current
BrightDate (6 d.p.).
%B is already bold, so BrightDate claims P for "Present."
PROMPT='[%P] %# '
# renders as:
# [9622.841738] %
history -d / fc -D
With EXTENDED_HISTORY set,
history -d (or -i, -f,
-E) prints command-execution timestamps as BrightDate
values instead of clock strings. fc -l -D reports
command durations in millidays instead of seconds
— so log analysis just becomes sort -n.
ls -l, stat
From bsh/files and bsh/stat.
bsh/stat is statically linked and autoloaded — no
zmodload required; bsh/files ships as a
dynamic module (zmodload bsh/files).
ls -l replaces the timestamp column with a BrightDate
decimal, following normal ls flags (-u for atime,
-c for ctime, etc.). The stat builtin
emits all time fields — atime, mtime,
ctime, and the new btime (birth time,
from st_birthtimespec on macOS/BSDs) — as BrightDate
decimals by default. Use -r for raw Unix seconds,
-r -s for both side-by-side, or
-F fmt for strftime.
-rw-r--r-- 1 jessica staff 55082 9628.198451 README.md
$ stat README.md
device 16777232
inode 117800042
mode -rw-r--r--
nlink 1
uid jessica
gid staff
rdev 0
size 55082
atime 9628.199840
mtime 9628.198451
ctime 9628.198451
blksize 4096
blocks 112
link
btime 9628.198451
$ stat +mtime README.md
9628.198451
$ stat -r -s README.md
mode 33188 (-rw-r--r--)
uid 501 (jessica)
gid 20 (staff)
atime 1778604397 (9628.199840)
mtime 1778604277 (9628.198451)
ctime 1778604277 (9628.198451)
btime 1778604277 (9628.198451)
sched
Schedule commands at future BrightDate times. Accepts traditional
HH:MM:SS offsets, BrightDate float offsets (+0.020833
≈ 30 min), and absolute BrightDate values. List pending events
with bare sched.
sched +0.020833 echo hello # ~30 min from now
sched 9628.5 echo midnight # absolute BD
$ sched
1 9627.930118 echo hello
2 9628.500000 echo midnight
times builtin
Reports user and system CPU time (for the shell and its children)
in millidays rather than the traditional m:ss format
— uniform units across every time surface in BSH.
The conversion engine is the
brightdate
Rust crate, statically linked into the bsh binary via
a tiny FFI surface. No runtime dependencies. No subprocess
overhead. Same TAI/J2000 substrate as the Rust and npm libraries.
Module index: bsh/brightdate bsh/datetime bsh/files bsh/stat bsh/brightlink
BSH extends zsh's glob qualifiers so .m, .a,
.c, and the new .b (birth time) accept
fractional decimal-day values. Filter files with
centiday precision (0.01 d = 864 s ≈ 14 min) directly in the shell —
and with .b, use a timestamp that touch
can never fake.
Start with two log files, old.log backdated 2 hours:
$ setopt extendedglob nullglob
$ touch old.log new.log
$ perl -e 'utime time()-7200, time()-7200, "old.log"'
$ echo *.log(.m-1) # within 1 day — both match, too broad
new.log old.log
$ echo *.log(.m-0.05) # within 0.05 d (~72 min) — precision cuts through
new.log
$ echo *.log(.m-0.01) # within centiday (~14 min) — only the fresh file
new.log
$ touch old.log # reset mtime — but birthtime is immutable
$ echo *.log(.m-0.01) # mtime fooled: both match now
new.log old.log
$ echo *.log(.b-0.01) # birthtime never lies — old.log was born 2 h ago
new.log
All unit suffixes accept fractional values: d (days,
default), h (hours), m (minutes),
s (seconds), w (weeks),
M (months). Combine with sort keys —
*.log(.b-1On) gives today's logs oldest-first;
*(ob) sorts everything by creation time.

BrightLink is an original BSH invention
— there is no upstream zsh equivalent.
bsh-inject
is a BSH-unique builtin that encrypts a credential payload read from
stdin and delivers it as a
LINK_DELIVER
JSON request over a Unix socket to the
BrightNexus
bridge. The plaintext never appears in ps, the shell
environment, command history, or terminal scrollback. BrightNexus
decrypts the payload with the session key it negotiated at
registration and surfaces the credential in its menu-bar dropdown
and Dashboard view, gated by a TTL after which it disappears.
Scope — what this is (and isn't).
BrightLink is designed for
short-lived developer credentials: database passwords
for local services, ephemeral API tokens, throwaway login creds,
STS sessions — the secrets you'd otherwise paste into a terminal,
export into $ENV, or stash in a .env file.
It is not a password manager: there is no vault, no
master-password unlock, no browser autofill, no sync between machines,
no long-term storage. Payloads live in the agent's memory only, are
gated by a ttl (default 300 s, configurable up to a
1h ceiling), and vanish on session expiry. For long-term credential
storage use 1Password / Bitwarden / Keychain. Use
BrightLink for the moment you'd otherwise leak a secret into shell
history or ps.
BrightNexus for macOS: Website · GitHub
bsh connects to BrightNexus on
~/.brightchain/brightnexus/brightnexus.sock and
registers via LINK_REGISTER. The bridge signs a
238-byte canonical transcript with its
Apple Secure Enclave P-256
key; bsh verifies against a TOFU-pinned SEP public key.
Both sides derive the 32-byte
K_session via bilateral HKDF-SHA256 over
clientShare ‖ bridgeShare. The key is never
transmitted and is wiped with
OPENSSL_cleanse() on shell exit.
bsh-inject reads stdin and encrypts it with
AES-256-GCM
under K_session. The
dir_tag, --type,
--context, and a monotonic
c_shell_to_agent counter are passed as
length-prefixed GCM
AAD —
cross-direction replay produces tag failure, captured
requests cannot be re-submitted, and counters within a
1000-step window enforce strict monotonicity.
bsh-inject sends a
LINK_DELIVER
JSON request — {cmd, counter, type, context, iv,
ciphertext, authTag} — to the bridge over the
same Unix socket the registration handshake travelled on.
Nothing is written to stdout, the user's terminal
scrollback, or any other byte stream. If the bridge is
unavailable, bsh-inject fails closed and
refuses to fall back to plaintext.
BrightNexus verifies the GCM auth tag (covering
dir_tag, counter, type, context), checks the
counter for replays, decodes the JSON body, and drops the
payload into its in-memory
EphemeralStore. Active credentials appear in
the menu-bar Credentials submenu and the Dashboard
Credentials view with click-to-copy and a live TTL
countdown. Entries auto-evict after their declared
ttl; nothing persists to disk.
Full specification: RFC — BrightLink Protocol
The CLI tools and BSH itself are available via the Digital Defiance tap:
brew tap digital-defiance/tap
brew install bdate btime buptime bcal bwatch bsh
sudo add-apt-repository ppa:digital-defiance/ppa
sudo apt update
sudo apt install bsh-shell
chsh -s /usr/bin/bsh
BSH builds with the standard autoconf workflow inherited from zsh,
plus a Rust toolchain for the
brightdate-rust static library.
A C compiler (clang or gcc),
make, autoconf, and a recent Rust
toolchain (rustup,
stable channel). macOS, Linux, and *BSD are all supported.
git clone --recursive https://github.com/Digital-Defiance/bsh.git
cd bsh
./configure
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
make check # optional: run the test suite
sudo make install
echo "$(command -v bsh)" | sudo tee -a /etc/shells
chsh -s "$(command -v bsh)"
bsh -c 'date' # BrightDate, baked in
bsh -c 'echo $BRIGHTEPOCH'
bsh -c 'PROMPT="[%P] %# "; print -P "$PROMPT"'
Looking for a configuration framework? See Oh My Bsh — 300+ plugins and 150+ themes layered on top of BSH.
@brightchain/brightdateTypeScript/JavaScript library. Same J2000.0 / TAI semantics. npm · source
brightdate
Rust crate powering BSH internally. Also ships standalone CLI
tools (bdate,
btime, buptime, bcal,
bwatch) — the b- prefixed versions for
use outside BSH, where the native date
etc. still rule.
crates.io
·
source
The CLI tools and BSH itself are available via the Digital Defiance tap:
brew tap digital-defiance/tap
brew install bdate btime buptime bcal bwatch bsh
BSH is available on Ubuntu and Debian via a Personal Package Archive:
sudo add-apt-repository ppa:digital-defiance/ppa
sudo apt update
sudo apt install bsh-shell
chsh -s /usr/bin/bsh