@@ -1,6 +1,117 @@
# Huorn Development Journal
## 2026-03-20 — Phase 2: Distribution, Admin & Security
### What was built
Everything needed to make Huorn installable by real users and manageable by server admins. Renamed the project from `alacrittymc` to `huorn`/`huorn-minecraft`, added admin infrastructure, and built the foundation for sandboxed terminal backends.
### Rename
Full rename across the codebase. Naming rule: things inside Minecraft (mod ID, package, commands, permissions) use `huorn`. Things outside Minecraft (native library, Rust crate, repo) use `huorn-minecraft`. The upstream `alacritty_terminal` crate dependency name is preserved.
### CI/Release Pipeline
- CalVer versioning: `YYYY.MM.BUILD` (e.g., `2026.03.1`)
- `ci/release.sh` computes next version from latest Anvil release tag
- `.anvil-ci.yml` pipeline: compute version → build natives → build JARs → test → create Anvil release → tag commit
- `gradle.properties` defaults to `0.0.0-dev` for local builds, CI overrides with `-Pmod_version=`
### Permission System
- 6 permission nodes: `huorn.use`, `huorn.use.docker`, `huorn.admin.{reload,list,kill,audit}`
- Fabric: `fabric-permissions-api` (LuckPerms-compatible, falls back to op level)
- Forge: vanilla op level fallback (full PermissionNode registration deferred)
- `@ExpectPlatform` abstraction bridges Fabric and Forge implementations
- All permission checks are server-side. `TerminalBlock.use()` checks `enableOnServers` config and `HuornPermissions.hasPermission()` before allowing interaction.
### Config
Restructured from flat fields to nested JSON:
```
server: enableOnServers, maxTerminalsPerPlayer, maxTerminalsTotal, idleTimeoutMinutes, defaultBackend, defaultOpLevel
backends: plain {enabled, allowedShells}, docker {enabled, image, memoryLimit, cpuLimit, networkEnabled, mountPaths}
security: commandBlocklist, auditLog {enabled, logFile, logCommands, logConnections}
display: fontSize, craftable
```
Old `canUse()` permission method removed. Old `allowedPlayers`/`opsAlwaysAllowed` fields removed. Config migration: logs warning if old `config/alacrittymc.json` exists.
### Terminal Management
`TerminalManager` singleton with `ConcurrentHashMap<UUID, AtomicInteger>` per-player counts + global `AtomicInteger`. Checks both `maxTerminalsPerPlayer` and `maxTerminalsTotal` before allowing terminal creation. Tracks active sessions with metadata (player, backend, location, start time).
### Admin Commands
Brigadier command tree registered via Architectury's `CommandRegistrationEvent`:
```
/huorn reload — reload config from disk
/huorn list — show active terminals (player, location, backend, uptime)
/huorn kill <player|all> — force-kill terminals
/huorn status — server-wide stats
/huorn audit [player] — tail last 20 audit log entries, color-coded
```
### Pluggable Sandbox Architecture
Rust trait system for terminal backends:
```rust
trait TerminalBackend: Send + Sync {
fn spawn(&self, config: &BackendConfig) -> Result<Box<dyn TerminalSession>>;
fn name(&self) -> &'static str;
fn is_available(&self) -> Result<bool>;
}
trait TerminalSession: Send {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn write(&mut self, data: &[u8]) -> Result<usize>;
fn resize(&mut self, cols: u16, rows: u16) -> Result<()>;
fn kill(&mut self) -> Result<()>;
fn is_alive(&self) -> bool;
}
# Alacritty-Minecraft Development Journal
```
- `PlainShellBackend`: extracted from `terminal.rs`, wraps `alacritty_terminal::tty::Pty`
- `DockerBackend`: stub (checks `/var/run/docker.sock`, spawn returns error — full implementation is next phase)
- `BackendRegistry` maps backend names to implementations
- `TerminalState` now holds `Box<dyn TerminalSession>` instead of direct `tty::Pty`
- JNI `native_create` accepts `backend` parameter, looks up in registry
- Backend selection: respects `defaultBackend` config + `huorn.use.docker` permission
## 2026-03-19/20 — Full Implementation & Working In-Game Terminal
### Audit Logging
- `AuditLogger`: JSONL writer to `logs/huorn-audit.log`, thread-safe via `Mutex<File>`
- Events: CONNECT, DISCONNECT, COMMAND, TIMEOUT, ADMIN_KILL, BLOCKED, BACKEND_ERROR
- Single writer architecture: all writes go through Rust. Java sends lifecycle events via JNI (`nativeAuditEvent`, `nativeAuditEventGlobal`)
- `InputFilter`: line-buffered command blocklist with substring matching
- `IdleTracker`: per-session last-activity timestamp tracking (background reaper thread deferred)
### Tests
65 Rust tests pass: 52 unit (rendering, colors, keys, terminal, backends) + 6 audit + 6 security + 1 integration.
### Files
12 commits on `feature/huorn-distribution-admin`:
1. Rust crate + native lib rename
2. Java packages + class rename
3. Resources + metadata rename
4. Documentation cleanup
5. Config restructure
6. CI pipeline (CalVer + Anvil CI)
7. Permission system (@ExpectPlatform)
8. TerminalManager + admin commands
9. Sandbox architecture (backend traits)
10. Audit logging + security
11. Integration (backend selection + audit command)
### Bugs found and fixed during real execution testing
1. **Native library not rebuilt after rename** — The `.dylib` in `natives/` still had old JNI class path `io/fangorn/alacrittymc/nativelib/NativeTerminal`. `cargo test` and `./gradlew build` both pass — only GameTest (real server boot) catches this. Fix: rebuilt native lib.
2. **NativeTerminal class loading on server init** — `nativeInitAudit()` call in `HuornMod.init()` triggered `NativeTerminal.<clinit>` which calls `System.load()`. Crashes dedicated servers. Fix: defer audit init to first terminal creation.
3. **fabric-permissions-api upgrades Fabric Loader** — `0.3.1` transitively pulls `fabric-loader:0.15.10`, which brings a Mixin version that crashes with `NoSuchFieldError: JAVA_22`. Fix: exclude transitive loader dep.
### Real execution test results
- **65 Rust tests** pass (52 unit + 6 audit + 6 security + 1 integration)
- **46 Minecraft GameTests** pass — block placement, entity lifecycle, multi-block groups, config defaults (nested structure), NBT serialization, interaction, native JNI bridge (with `"plain"` backend parameter), terminal resize
- Gradle BUILD SUCCESSFUL for both Fabric and Forge JARs
### Deferred
- Docker backend full implementation (hyper/hyperlocal/tokio — separate ticket)
- MicroVM / Apple Hypervisor / Hyper-V backends
- Custom sandbox image on `registry.fangorn.io`
- Modrinth / CurseForge listing
- Background idle timeout reaper thread
- Full Forge PermissionNode registration
---
## 2026-03-19/20 — Phase 1: Full Implementation & Working In-Game Terminal
### What was built
A fully functional Alacritty terminal emulator embedded as an interactive block in Minecraft Java Edition 1.20.1, supporting both Fabric and Forge via Architectury.
@@ -36,7 +147,7 @@
- TerminalScreen (F12 full-screen overlay)
- TerminalInputHandler (GLFW → ANSI), TerminalFocusHandler
- KeyboardHandlerMixin (charTyped interception)
- AlacrittyMod (registry), AlacrittyModClient (events)
- HuornMod (registry), HuornModClient (events)
- Fabric + Forge entrypoints
**Tests** (70+ automated):
@@ -56,6 +167,6 @@
```bash
# Build
cd rust && cargo build --release
cp target/release/libhuorn_minecraft.dylib ../common/src/main/resources/natives/macos-aarch64/
cp target/release/libalacritty_minecraft.dylib ../common/src/main/resources/natives/macos-aarch64/
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home
./gradlew build
@@ -64,6 +175,6 @@
./gradlew :fabric:runClient
# In-game
/give @s huorn:terminal_block
/give @s alacrittymc:terminal_block
# Place block, right-click to start terminal
# ESC to exit, F12 for full-screen overlay