Skip to content

enkron/enkron.github.io

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

221 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status

Static site generator

Rust-based static site generator that converts Markdown to HTML and PDF. Includes a CLI for content management and browser-side (WASM) decryption of password-protected entries.

Regular pages ship zero JavaScript: the day/night theme follows the OS setting via pure CSS (prefers-color-scheme), and the WASM module is loaded only on pages with encrypted content.

    flowchart LR;
    A[content in Markdown]-->B[static site generator];
    C[HTML templates]-->B;
    B-->D[index.html];
    B-->E[PDF files];
Loading

Usage

Build site

Generate HTML and PDF files from Markdown sources:

cargo run --release

Add blog entry

Create a new entry in in/entries/ and update in/junkyard.md:

cargo run --release -- add "Entry Title"

Entry filename format: N-entry-title.md where N is auto-incremented.

Add shadow entry (private)

Create a private entry that's not listed in junkyard.md:

cargo run --release -- add --shadow "Private Entry"

Shadow entries:

  • Stored in in/entries/shadow/ (independent numbering)
  • Output to priv/entries/N.html (separate from pub/)
  • Accessible via /priv/entries/N.html URLs
  • Not added to junkyard index
  • Navigation links only to other shadow entries

Edit blog entry

Edit an existing entry by number or path:

# Edit public entry #5
cargo run --release -- edit 5
cargo run --release -- edit 5p

# Edit shadow entry #5
cargo run --release -- edit 5s

# Edit by full path
cargo run --release -- edit in/entries/5-title.md

Entry editing:

  • Opens entry in $EDITOR (defaults to vim)
  • Auto-decrypts locked entries (.enc) to temporary file
  • Auto-re-encrypts after editing (preserves encryption)
  • Temp files cleaned up automatically (RAII pattern)
  • Single passphrase attempt (fail-fast security)

Encrypt blog entry

Password-protect an entry with AES-256-GCM encryption:

# Create and encrypt an entry
cargo run --release -- add "Private Research"
cargo run --release -- lock in/entries/7-private-research.md

# Encrypt shadow entry (double privacy: unlisted + encrypted)
cargo run --release -- add --shadow "Secret Notes"
cargo run --release -- lock in/entries/shadow/1-secret-notes.md

# Decrypt entry back to plaintext
cargo run --release -- lock --unlock in/entries/7-private-research.enc

Encryption details:

  • Uses AES-256-GCM (authenticated encryption, tamper-proof)
  • Argon2id key derivation (64MB memory, GPU-resistant)
  • Source file: .md.enc (encrypted on disk)
  • Browser-side decryption via WASM (no server needed)
  • Locked entries show blurred preview with unlock form
  • Set passphrase: export RONIO_LOCK_KEY="your-passphrase" (interactive no-echo prompt when unset; there is deliberately no CLI flag because flags are visible in the process list)
  • No passphrase is needed to build the site: .enc files are embedded as-is

Lockfile tracking:

  • Locking an entry records it in .ronio-locks (JSON, gitignored)
  • Tracks entry number, shadow flag, and creation timestamp
  • Metadata only; .enc files themselves ARE committed to the repository

Work period macros

Markdown sources may embed duration markers that are expanded at build time (used by cv.md):

{{work_period: start="2022-12", end="present"}}   -> "2 years, 7 months"
{{work_period: start="2018-07", end="2021-11"}}   -> "3 years, 4 months"
{{total_work_period}}                             -> sum of all markers, years only

{{total_work_period}} sums the markers of the current file, or falls back to scanning in/cv.md when the current file has none.

CLI reference

ronio [COMMAND]        # no command: build the site

Commands:
  add [OPTIONS] <TITLE>    Add a new blog entry
  edit <TARGET>            Edit existing entry (5p/5s/5 or full path)
  lock [OPTIONS] <PATH>    Encrypt/decrypt entry with AES-256-GCM
  help                     Print help information

Options for add:
  --shadow                 Create as shadow entry (private, not listed)
  -h, --help               Print help

Options for lock:
  -u, --unlock             Decrypt .enc file back to .md
  -h, --help               Print help

Global options:
  -h, --help               Print help (see subcommand help for details)
  -V, --version            Print version

Run cargo run --release -- help <COMMAND> for the long per-command help, including environment variables (RONIO_LOCK_KEY, EDITOR).

Project structure

in/
├── entries/           Blog entries (numbered: 1-title.md, 2-title.md, ...)
│   └── shadow/       Private entries (not in junkyard)
├── cv.md             CV (→ root/cv.html + download/sbelokon.pdf)
├── index.md          Cover page (→ root/index.html + download/cover.pdf)
└── junkyard.md       Blog index page

pub/
├── entries/          Generated entry HTML (1.html, 2.html, ...)
└── junkyard.html     Blog index HTML

priv/
└── entries/          Generated shadow entry HTML (1.html, 2.html, ...)

download/             Generated PDFs

web/pkg/              WASM module (locked entry decryption only)

404.html              Custom 404 page

Every build also generates directory index stubs so hosting never exposes a directory listing:

  • pub/index.html, pub/entries/index.html: redirect to the junkyard
  • download/index.html: redirects to the home page
  • priv/entries/index.html: 404-style page (shadow entries stay unlisted)

Theme

The site has light and dark palettes defined as CSS variables in css/main.css. The dark palette activates through a single @media (prefers-color-scheme: dark) block, so pages follow the OS theme with no JavaScript, no toggle button, and no flash of the wrong theme on load. Matching <meta name="theme-color"> tags keep the browser chrome in sync on mobile.

Development

Build and serve locally

make site

Builds WASM module, generates site, serves on http://localhost:8080.

Build WASM module

wasm-pack build --target web --out-dir web/pkg

Clean artifacts

make clean

Smoke test the lock workflow

make test

Creates a "Test Entry", appends a marker line, and encrypts it with the passphrase test. Clean up afterwards with make clean.

Code quality

cargo check
cargo clippy
./pre-ci.sh    # clippy + release build + wasm-pack build + tests (mirrors CI)

Git hooks

Install pre-push hook to validate code quality before pushing:

./hooks/install-hooks.sh

Pre-push hook runs automatically before git push and checks:

  • Code formatting (cargo fmt)
  • Linting (cargo clippy)
  • Test suite (cargo test)

To bypass hook (not recommended):

git push --no-verify

Known improvement backlog

Larger refactors identified during the 2026-07 code review, deliberately not yet applied:

  • Unify the duplicated AES-GCM/Argon2 decryption logic and parameter constants between src/crypto.rs (native) and src/lib.rs (WASM)
  • Unify the two markdown renderers: pulldown-cmark (build) vs the minimal hand-rolled converter used for browser-decrypted content in src/lib.rs
  • Split Site::build into discovery/routing/rendering functions
  • Migrate rand 0.8 to 0.9 (thread_rng/distributions are renamed there)
  • Make lock/unlock atomic: write the output file and fsync before removing the source, so a crash cannot lose both
  • Consider Rust edition 2024

CI/CD

Implemented using Gihub workflows feature. Build stages:

    flowchart LR;
    A[provision VM/container]-->B[install Rust toolchains];
    B-->C[checkout repository];
    C-->D[build static pages];
    C-->E[site availability test];
    D-->F[pack artifacts];
    F-->G[deploy artifacts];
Loading

About

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors