aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-04-02 16:18:29 -0500
committerCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-04-02 16:18:29 -0500
commit48a837a91959efbc7da7f98447c50f32b4ee7c94 (patch)
tree0b17514bd37b6de76a8a497ef3ac3d6bf696ae53
parent871c22cae2ef84694502fde290838bb24432a1f4 (diff)
jta, other changes
-rw-r--r--AGENTS.md286
-rw-r--r--broken_stuff1
-rw-r--r--flake.lock19
-rw-r--r--flake.nix5
-rw-r--r--hosts/ganymede/config.nix10
-rw-r--r--hosts/mercury/config.nix2
-rw-r--r--hosts/mercury/hjem.nix2
-rw-r--r--hosts/mercury/nixos.nix13
-rw-r--r--modules/services/nixos/default.nix3
-rw-r--r--modules/services/nixos/jta/default.nix49
-rw-r--r--modules/services/nixos/jta/go.mod5
-rw-r--r--modules/services/nixos/jta/go.sum2
-rw-r--r--modules/services/nixos/jta/index.html129
-rw-r--r--modules/services/nixos/jta/main.go95
-rw-r--r--modules/services/nixos/jta/md_template.html11
-rw-r--r--modules/services/nixos/jta/pkg.nix6
-rw-r--r--modules/services/nixos/jta/style.css143
-rw-r--r--modules/services/nixos/openssh.nix2
-rw-r--r--modules/services/options.nix5
-rw-r--r--modules/terminal/hjem/programs/broot.nix33
20 files changed, 620 insertions, 201 deletions
diff --git a/AGENTS.md b/AGENTS.md
index dfd8a42..0c8da14 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,192 +1,154 @@
# AGENTS.md
-Guidance for agentic coding assistants working in this repository.
+Guidance for coding agents working in this repository.
-## 1) Project Snapshot
+## 1) Repository Purpose
+- NixOS flake repo with host configs, reusable modules, and local packages.
+- Most edits happen in `hosts/`, `modules/`, `lib/`, and `pkgs/`.
+- Includes a standalone Go project at `pkgs/jta/`.
+- Make focused changes; avoid unrelated refactors.
-- Repo type: NixOS flake-based configuration repo.
-- Core pattern: "homogenous modules" (shared `options.nix` + `nixos/default.nix` + `hjem/default.nix`).
-- Main namespace for custom options: `collinux`.
-- Hosts currently configured: `mercury`, `jupiter`, `ganymede`.
-- Deployment helper: custom `yo` command (Ruby wrapper around `nix`/`nom`).
+## 2) AI Rule Files (Cursor/Copilot)
+- `.cursor/rules/`: not found.
+- `.cursorrules`: not found.
+- `.github/copilot-instructions.md`: not found.
+- There are no repository-specific Cursor/Copilot instruction files to apply.
-## 2) Rule Files Discovery
+## 3) Key Paths
+- `flake.nix`: flake inputs/outputs and host definitions.
+- `hosts/<hostname>/config.nix`: host-level entrypoints.
+- `modules/`: homogenous module tree.
+- `lib/nix-furnace/mkSystem.nix`: system composition.
+- `lib/lib.nix`: shared helpers (`my-lib`).
+- `pkgs/yo/`: Ruby deployment/build helper.
+- `pkgs/yossh/`: Lua SSH tooling and tests.
+- `pkgs/jta/`: Go module.
+- `justfile`: command shortcuts.
-The following rule files were checked:
+## 4) Build Commands
+Run from `/home/collin/nixos` unless noted.
-- `.cursor/rules/**`: not found
-- `.cursorrules`: not found
-- `.github/copilot-instructions.md`: not found
+NixOS commands:
+- `yo build`
+- `yo build <hostname>`
+- `yo switch` or `yo sw`
+- `yo boot`
+- `yo test`
+- `yo deploy <ssh-target>` or `yo dep <ssh-target>`
-No Cursor/Copilot-specific rules are currently present in this repository.
+Direct Nix commands:
+- `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`
+- `nix build .#docs`
+- `nix build`
-## 3) Directory Landmarks
+Just shortcuts:
+- `just build`
+- `just switch`
+- `just deploy`
-- `flake.nix`: top-level inputs/outputs, host definitions.
-- `hosts/<hostname>/config.nix`: host-level declarative settings.
-- `modules/`: feature modules (desktop, terminal, services, system, etc.).
-- `lib/nix-furnace/mkSystem.nix`: module wiring and host assembly.
-- `lib/lib.nix`: custom helper library (`my-lib`).
-- `pkgs/yo/`: Ruby deploy/build helper package.
-- `pkgs/yossh/`: Lua SSH helper package + lightweight tests.
-- `docs/homogenous_modules.md`: architecture background.
+Go project (`pkgs/jta`):
+- `go build ./...`
+- `go run .`
-## 4) Build / Check / Deploy Commands
+## 5) Lint and Formatting
+Nix:
+- `nix run nixpkgs#alejandra -- .`
+- Prefer formatting only touched files.
-Run from repo root: `/home/collin/nixos`.
+Go (`pkgs/jta`):
+- `gofmt -w .`
+- `go vet ./...`
-### Core build commands
+Validation defaults:
+- After Nix changes, run a host build.
+- After Go changes, run build and tests.
-- Build current host and activate immediately:
- - `yo switch`
- - alias: `yo sw`
-- Build current host and activate on next boot:
- - `yo boot`
-- Build current host and test activation (non-persistent):
- - `yo test`
-- Build a specific host configuration without switching:
- - `yo build <hostname>`
-- Build current hostname (auto-detected):
- - `yo build`
+## 6) Test Commands
+Repository-wide:
+- No single root test harness is configured.
+- Nix host builds are primary verification.
-### Direct nix builds
+Nix checks:
+- `yo build <hostname>`
+- `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`
-- Build one host toplevel derivation:
- - `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`
-- Build generated docs package:
- - `nix build .#docs`
-- Build default package output:
- - `nix build`
+Go tests (`pkgs/jta`):
+- Run all tests: `go test ./...`
+- Run single test name: `go test ./... -run '^TestName$'`
+- Run single test in current package: `go test . -run '^TestName$'`
-### Deploy commands
+Lua tests (`pkgs/yossh`):
+- Run all defined tests: `lua pkgs/yossh/tests/unit_tests.lua`
+- Current single-test method is manual (temporarily call one test in `main()`).
-- Deploy via custom wrapper (see `pkgs/yo/default.nix` behavior):
- - `yo deploy <ssh-host-or-user@host>`
- - alias: `yo dep <ssh-host-or-user@host>`
+## 7) Architectural Conventions
+- Preserve homogenous module layout:
+ - `options.nix` for option definitions.
+ - `nixos/default.nix` for NixOS implementation.
+ - `hjem/default.nix` for hjem implementation.
+- Keep custom options under `collinux.*`.
+- Keep host configs declarative.
+- Reuse existing helper patterns before creating new abstractions.
-Note: Some docs mention `yo deploy <hostname> <ssh-host>`; current code path derives hostname
-from the SSH target argument.
+## 8) Code Style
+General:
+- Match style of the touched file.
+- Keep diffs minimal and intentional.
+- Prefer explicit naming over abbreviations.
+- Add comments only for non-obvious logic.
-## 5) Lint / Format / Validation
+Nix style:
+- 2-space indentation; no tabs.
+- Common arg pattern: `{ lib, config, pkgs, ... }:`.
+- Use `cfg = config.collinux.<path>;` when it improves clarity.
+- Use `lib.mkIf` for option-gated config.
+- Prefer typed options (`types.port`, `types.nullOr`, submodules).
+- Keep imports stable and readable (usually one per line).
+- Use `inherit (lib) ...` where idiomatic.
-There is no single repo-wide lint target committed (no Makefile/justfile/pre-commit config found).
+Imports/dependencies:
+- Prefer relative imports in module trees (`./foo.nix`).
+- Avoid circular dependencies.
+- Reuse `my-lib` helpers from `lib/lib.nix` when possible.
-Use these practical checks:
+Go style (`pkgs/jta`):
+- Keep code gofmt-clean.
+- Preserve gofmt import ordering/grouping.
+- Prefer early error returns.
+- Use standard `net/http` helpers consistently.
-- Nix syntax/eval sanity via host build:
- - `yo build <hostname>`
- - or `nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel`
-- Option docs generation as additional eval coverage:
- - `nix build .#docs`
-- Format Nix files with Alejandra (formatter used in editor config):
- - `nix run nixpkgs#alejandra -- .`
+Ruby style (`pkgs/yo`):
+- snake_case for methods/locals; CamelCase for classes/modules.
+- Validate CLI args and fail fast (`abort` with clear message).
+- Do not hide command failures.
-If you add new tool-specific linting, document the exact command in this file.
+Lua style (`pkgs/yossh`, `pkgs/util`):
+- Follow local naming conventions (mostly snake_case).
+- Keep helpers small and composable.
+- Raise clear errors instead of ambiguous nil behavior.
-## 6) Test Commands (Including Single-Test Guidance)
-
-### Repository-level tests
-
-- No centralized automated test suite is wired at repo root.
-- The most test-like validation is successful Nix evaluation/build per host.
-
-### `pkgs/yossh` Lua tests
-
-- Test file exists: `pkgs/yossh/tests/unit_tests.lua`.
-- Run file directly:
- - `lua pkgs/yossh/tests/unit_tests.lua`
-
-Important: current file defines tests and `main()` but does not call `main()` at EOF.
-Running it as-is may not execute assertions unless the file is updated.
-
-### Running a single test
-
-There is no built-in single-test runner flag today.
-
-Recommended workflow for single-test execution:
-
-1. Temporarily edit `pkgs/yossh/tests/unit_tests.lua` so `main()` calls only one test function.
-2. Run `lua pkgs/yossh/tests/unit_tests.lua`.
-3. Revert the temporary test-selection edit before committing.
-
-If you frequently need single-test runs, introduce a minimal CLI test selector and document it here.
-
-## 7) Architecture Conventions You Must Preserve
-
-- Keep host configs declarative attribute sets, not functions.
-- Keep module option definitions in `options.nix` files.
-- Keep NixOS implementation under `nixos/default.nix`.
-- Keep hjem implementation under `hjem/default.nix`.
-- Prefer adding features by extending existing module trees, not ad-hoc host logic.
-- Use `my-lib` helpers when patterns already exist (`mkProgramOption`, `mkThemeOption`, etc.).
-- Keep custom options under `collinux.*` to avoid collisions.
-
-## 8) Code Style Guide
-
-### General
-
-- Match existing style in touched files; do not mass-reformat unrelated code.
-- Keep diffs focused and minimal.
-- Prefer explicit, descriptive names over abbreviations.
-- Avoid adding comments unless logic is non-obvious.
-
-### Nix style
-
-- Indentation: 2 spaces; no tabs.
-- Favor argument destructuring at top:
- - `{ pkgs, lib, config, ... }:` style.
-- Common structure:
- - bind `cfg = config.collinux.<path>;` in `let`.
- - gate behavior with `lib.mkIf cfg.enable` where relevant.
-- Prefer `inherit (lib) ...` for imported lib symbols.
-- Keep `imports` lists clean and stable; one module per line.
-- Use `mkOption`/`mkEnableOption` for options; include `description`, `type`, and sensible defaults.
-- Use precise types (`types.port`, custom net types, `nullOr`, submodules) rather than loose strings.
-- Prefer pure expressions and deterministic outputs.
-
-### Import and dependency conventions
-
-- Reuse local helpers from `my-lib` before adding new helper patterns.
-- In modules, keep imports relative and predictable (`./foo.nix`, `./default.nix`).
-- Avoid circular/implicit dependencies across module families.
-
-### Naming conventions
-
-- Nix option paths: lower camelCase segments under `collinux`.
-- Files/modules: lowercase names, hyphenated when appropriate.
-- Lua locals/functions: mostly snake_case in core library; preserve local file conventions.
-- Ruby methods: snake_case; class/module names: CamelCase.
-
-### Types and validation
-
-- Add/keep explicit option types whenever possible.
-- Use custom validators from `my-lib.netTypes` for network values.
-- For structured config, prefer typed submodules over freeform attrs.
-
-### Error handling
-
-- Fail early with clear messages (`assertions`, explicit `error`/`raise`).
-- In scripts/wrappers, check command success and raise on failure.
-- Do not swallow errors from builds, copy, deploy, or switch operations.
-
-### Secrets and security
+## 9) Types, Naming, and Error Handling
+- Use explicit Nix option types whenever practical.
+- Option naming: lower camelCase segments under `collinux`.
+- File naming: lowercase; hyphenate when local pattern uses it.
+- Fail early with actionable error messages.
+- Do not swallow build/deploy/switch failures.
+## 10) Secrets and Safety
- Never commit plaintext secrets.
-- Use agenix-managed `.age` files and declared secret paths.
-- Preserve service hardening settings unless there is a clear reason to change.
-
-## 9) Agent Workflow Expectations
-
-- Before changing behavior, read nearby module/options files to follow local patterns.
-- Prefer validating with a targeted host build after Nix edits.
-- If touching deployment logic, test with non-destructive build paths first.
-- Keep commit scope tight: one logical change per commit.
-- Update docs when command behavior or module patterns change.
+- Preserve agenix and `.age` workflows.
+- Treat `hosts/*/secrets/*` and `*.age` as sensitive.
+- Do not weaken security defaults without explicit intent.
-## 10) Definition of Done (for automated agents)
+## 11) Workflow for Agents
+- Read adjacent files before editing.
+- Follow established patterns first.
+- Validate the smallest meaningful scope.
+- Update docs when command behavior changes.
-- Code compiles/evaluates for affected host(s) via `yo build <hostname>` or equivalent.
-- New/changed options are typed and documented by structure.
-- Formatting is consistent with current repository conventions.
+## 12) Definition of Done
+- Affected targets build/evaluate successfully.
+- Formatting and style are consistent.
+- New options are typed appropriately.
- No plaintext secrets introduced.
-- Any new commands, checks, or test entrypoints are reflected in this `AGENTS.md`.
+- This AGENTS guide remains accurate.
diff --git a/broken_stuff b/broken_stuff
new file mode 100644
index 0000000..84c3381
--- /dev/null
+++ b/broken_stuff
@@ -0,0 +1 @@
+- automount filesganymede-dav (code removed)
diff --git a/flake.lock b/flake.lock
index 65c8570..c7b97b8 100644
--- a/flake.lock
+++ b/flake.lock
@@ -405,8 +405,7 @@
"nixos-facter-modules": "nixos-facter-modules",
"nixpkgs": "nixpkgs",
"tmux-tsunami": "tmux-tsunami",
- "uc-css-js": "uc-css-js",
- "yoshi-lua": "yoshi-lua"
+ "uc-css-js": "uc-css-js"
}
},
"rust-overlay": {
@@ -541,22 +540,6 @@
"repo": "uc.css.js",
"type": "github"
}
- },
- "yoshi-lua": {
- "flake": false,
- "locked": {
- "lastModified": 1772547188,
- "narHash": "sha256-vmuc/G4tF6WMHUMlHYmwctPRjT+1u15aO0W6xwPALX8=",
- "owner": "bluedragon1221",
- "repo": "yoshi",
- "rev": "fa789d321c1fb4f97755bc199f1666836a87bdcd",
- "type": "github"
- },
- "original": {
- "owner": "bluedragon1221",
- "repo": "yoshi",
- "type": "github"
- }
}
},
"root": "root",
diff --git a/flake.nix b/flake.nix
index 0c64294..0cd3cd9 100644
--- a/flake.nix
+++ b/flake.nix
@@ -43,11 +43,6 @@
inputs.nixpkgs.follows = "nixpkgs";
};
- yoshi-lua = {
- url = "github:bluedragon1221/yoshi";
- flake = false;
- };
-
# firefox modding stuff
firefox-csshacks = {
url = "github:MrOtherGuy/firefox-csshacks";
diff --git a/hosts/ganymede/config.nix b/hosts/ganymede/config.nix
index c4d9109..f4c4430 100644
--- a/hosts/ganymede/config.nix
+++ b/hosts/ganymede/config.nix
@@ -69,11 +69,6 @@
privateUrl = "git.ganymede";
};
- # agate = {
- # enable = true;
- # publicUrl = "williamsfam.us.com";
- # };
-
minecraft = {
enable = true;
listenAddr = "0.0.0.0";
@@ -96,6 +91,11 @@
};
};
+ jta = {
+ enable = true;
+ publicUrl = "jta.williamsfam.us.com";
+ };
+
caddy = {
enable = true;
envFile = config.collinux.secrets."caddy-env".path;
diff --git a/hosts/mercury/config.nix b/hosts/mercury/config.nix
index d2164c4..852c393 100644
--- a/hosts/mercury/config.nix
+++ b/hosts/mercury/config.nix
@@ -23,7 +23,7 @@
components = {
fuzzel.enable = true;
dunst.enable = true;
- tofi.enable = true;
+ # tofi.enable = true;
};
};
diff --git a/hosts/mercury/hjem.nix b/hosts/mercury/hjem.nix
index afdf861..91e9c4b 100644
--- a/hosts/mercury/hjem.nix
+++ b/hosts/mercury/hjem.nix
@@ -7,11 +7,11 @@
prismlauncher
mpv
- bluetuith
opencode
(pkgs.callPackage ../../pkgs/yo {})
+ (pkgs.callPackage ../../pkgs/yokey {})
captive-browser # https://words.filippo.io/captive-browser
(pkgs.makeDesktopItem {
diff --git a/hosts/mercury/nixos.nix b/hosts/mercury/nixos.nix
index 8fcb504..6459fdb 100644
--- a/hosts/mercury/nixos.nix
+++ b/hosts/mercury/nixos.nix
@@ -22,6 +22,19 @@
dataDir = "/home/collin/.local/syncthing";
};
+ fileSystems."/home/collin/ganymede" = {
+ device = "collin@ganymede:/media";
+ fsType = "fuse.sshfs";
+ options = [
+ "identityfile=/home/collin/.ssh/id_ed25519"
+ "idmap=user"
+ "x-systemd.automount" #< mount the filesystem automatically on first access
+ "allow_other" #< don't restrict access to only the user which `mount`s it (because that's probably systemd who mounts it, not you)
+ "user" #< allow manual `mount`ing, as ordinary user.
+ ];
+ };
+ boot.supportedFilesystems."fuse.sshfs" = true;
+
services.autossh.sessions = [
{
name = "ganymede";
diff --git a/modules/services/nixos/default.nix b/modules/services/nixos/default.nix
index d0e170c..32efb1c 100644
--- a/modules/services/nixos/default.nix
+++ b/modules/services/nixos/default.nix
@@ -8,9 +8,8 @@
./btopweb.nix
./cgit
+ ./jta
./polaris.nix
- # ./mopidy.nix
- # ./jellyfin.nix
./agate.nix
./minecraft.nix
./copyparty.nix
diff --git a/modules/services/nixos/jta/default.nix b/modules/services/nixos/jta/default.nix
new file mode 100644
index 0000000..c1ea012
--- /dev/null
+++ b/modules/services/nixos/jta/default.nix
@@ -0,0 +1,49 @@
+{
+ pkgs,
+ config,
+ lib,
+ ...
+}: let
+ cfg = config.collinux.services.jta;
+
+ package = pkgs.callPackage ./pkg.nix {};
+in {
+ imports = [
+ (import ../mkCaddyCfg.nix cfg)
+ ];
+
+ config = lib.mkIf cfg.enable {
+ users.groups."jta" = {};
+ users.users."jta" = {
+ isSystemUser = true;
+ group = "jta";
+ extraGroups = ["fileserver"];
+
+ home = "/var/lib/jta";
+ createHome = true;
+ homeMode = "755";
+ };
+
+ systemd.services."jta" = {
+ description = "Juksere trives aldri, kids";
+ restartIfChanged = true;
+ wants = ["network-online.target" "caddy.service"];
+ after = ["network-online.target" "caddy.service"];
+
+ environment = {
+ PORT = toString cfg.port;
+ ROOT_DIR = "/media/jta";
+ };
+
+ serviceConfig = {
+ User = "jta";
+ Type = "simple";
+
+ WorkingDirectory = "/var/lib/jta";
+ ExecStart = "${package}/bin/jta";
+ };
+
+ wantedBy = ["multi-user.target"];
+ };
+ };
+}
diff --git a/modules/services/nixos/jta/go.mod b/modules/services/nixos/jta/go.mod
new file mode 100644
index 0000000..8f27af1
--- /dev/null
+++ b/modules/services/nixos/jta/go.mod
@@ -0,0 +1,5 @@
+module git.ganymede/jta
+
+go 1.25.5
+
+require github.com/yuin/goldmark v1.8.2
diff --git a/modules/services/nixos/jta/go.sum b/modules/services/nixos/jta/go.sum
new file mode 100644
index 0000000..6a37955
--- /dev/null
+++ b/modules/services/nixos/jta/go.sum
@@ -0,0 +1,2 @@
+github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
+github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
diff --git a/modules/services/nixos/jta/index.html b/modules/services/nixos/jta/index.html
new file mode 100644
index 0000000..fd93de6
--- /dev/null
+++ b/modules/services/nixos/jta/index.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>jta@ganymede</title>
+ <link rel="stylesheet" href="style.css">
+ <style>
+ body {
+ margin: 0;
+ max-width: none;
+ padding: 0;
+ background: var(--bg);
+ }
+
+ header {
+ padding: 1.25rem 1.5rem;
+ text-align: center;
+ font-family: var(--font-heading);
+ font-size: var(--step-2);
+ line-height: 1.2;
+ letter-spacing: -0.01em;
+ font-weight: 700;
+ background: #020617;
+ border-bottom: 1px solid var(--border);
+ }
+
+ .section {
+ padding: 1rem;
+ max-width: 1100px;
+ margin: 0 auto;
+ }
+
+ .section-title {
+ font-size: var(--step-1);
+ line-height: 1.25;
+ letter-spacing: -0.01em;
+ margin: 1rem 0 0.5rem 0.5rem;
+ color: var(--text-soft);
+ }
+
+ .container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
+ gap: 1rem;
+ padding: 0 1rem 1rem 1rem;
+ }
+
+ .card {
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 1rem;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
+ cursor: pointer;
+ text-decoration: none;
+ color: var(--text);
+ display: block;
+ }
+
+ .card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 10px 20px rgba(0,0,0,0.6);
+ }
+
+ .title { font-size: var(--step-1); line-height: 1.3; letter-spacing: -0.01em; font-weight: 600; margin-bottom: 0.5rem; }
+ .due { font-size: var(--step--1); color: var(--muted); margin-bottom: 0.5rem; }
+ .desc { font-size: var(--step--1); color: var(--text-soft); line-height: 1.55; }
+ </style>
+ </head>
+ <body>
+ <header>Juksere Trives Aldri</header>
+
+ <div id="app"></div>
+
+ <script>
+ const app = document.getElementById("app");
+
+ function renderHome(data) {
+ // Group by class
+ const groups = {};
+ data.forEach(item => {
+ const cls = item.class || "Other";
+ if (!groups[cls]) groups[cls] = [];
+ groups[cls].push(item);
+ });
+
+ // Render sections
+ Object.entries(groups).forEach(([cls, items]) => {
+ const section = document.createElement("div");
+ section.className = "section";
+
+ const title = document.createElement("div");
+ title.className = "section-title";
+ title.textContent = cls;
+ section.appendChild(title);
+
+ const container = document.createElement("div");
+ container.className = "container";
+
+ items.forEach(item => {
+ const card = document.createElement("a");
+ card.className = "card";
+ card.href = item.link || "#";
+
+ card.innerHTML = `
+ <div class="title">${item.title}</div>
+ <div class="due">Due: ${item.due}</div>
+ ${item.description ? `<div class="desc">${item.description}</div>` : ""}
+ `;
+
+ container.appendChild(card);
+ });
+
+ section.appendChild(container);
+ app.appendChild(section);
+ });
+ }
+
+ fetch("assignments.json")
+ .then(res => res.json())
+ .then(data => renderHome(data))
+ .catch(() => {
+ app.innerHTML = "<p style='padding:1rem;'>Failed to load data.</p>";
+ });
+ </script>
+ </body>
+</html>
diff --git a/modules/services/nixos/jta/main.go b/modules/services/nixos/jta/main.go
new file mode 100644
index 0000000..3e5fc1f
--- /dev/null
+++ b/modules/services/nixos/jta/main.go
@@ -0,0 +1,95 @@
+package main
+
+import (
+ "bytes"
+ _ "embed"
+ "html/template"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
+)
+
+// Configure Goldmark with GitHub Flavored Markdown
+var mdParser = goldmark.New(
+ goldmark.WithExtensions(
+ extension.GFM,
+ ),
+)
+
+//go:embed md_template.html
+var mdTemplate string
+
+//go:embed style.css
+var styleCSS string
+
+//go:embed index.html
+var indexHTML string
+
+var pageTmpl = template.Must(template.New("md_template.html").Parse(mdTemplate))
+
+func serveMarkdown(w http.ResponseWriter, r *http.Request) {
+ // Prevent path traversal
+ clean := filepath.Clean(r.URL.Path)
+ path := filepath.Join(rootDir, strings.TrimPrefix(clean, "/"))
+
+ mdBytes, err := os.ReadFile(path)
+ if err != nil {
+ http.NotFound(w, r)
+ return
+ }
+
+ var buf bytes.Buffer
+ if err := mdParser.Convert(mdBytes, &buf); err != nil {
+ http.Error(w, "failed to render markdown", 500)
+ return
+ }
+
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+
+ pageTmpl.Execute(w, map[string]template.HTML{
+ "Content": template.HTML(buf.String()),
+ })
+}
+
+var rootDir = "."
+
+func main() {
+ if envRootDir := os.Getenv("ROOT_DIR"); envRootDir != "" {
+ rootDir = envRootDir
+ }
+
+ fs := http.FileServer(http.Dir(rootDir))
+ port := os.Getenv("PORT")
+ if port == "" {
+ port = "8080"
+ }
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/" || r.URL.Path == "/index.html" {
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ w.Write([]byte(indexHTML))
+ return
+ }
+
+ if r.URL.Path == "/style.css" {
+ w.Header().Set("Content-Type", "text/css; charset=utf-8")
+ w.Write([]byte(styleCSS))
+ return
+ }
+
+ // Intercept markdown files
+ if strings.HasSuffix(r.URL.Path, ".md") {
+ serveMarkdown(w, r)
+ return
+ }
+
+ // Otherwise serve normally
+ fs.ServeHTTP(w, r)
+ })
+
+ http.ListenAndServe(":"+port, nil)
+}
diff --git a/modules/services/nixos/jta/md_template.html b/modules/services/nixos/jta/md_template.html
new file mode 100644
index 0000000..daa57b7
--- /dev/null
+++ b/modules/services/nixos/jta/md_template.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>JTA</title>
+ <link rel="stylesheet" href="/style.css">
+ </head>
+ <body>
+ {{.Content}}
+ </body>
+</html>
diff --git a/modules/services/nixos/jta/pkg.nix b/modules/services/nixos/jta/pkg.nix
new file mode 100644
index 0000000..c8f7d77
--- /dev/null
+++ b/modules/services/nixos/jta/pkg.nix
@@ -0,0 +1,6 @@
+{pkgs ? import <nixpkgs> {system = "x86_64-linux";}, ...}:
+pkgs.buildGoModule {
+ name = "jta";
+ src = ./.;
+ vendorHash = "sha256-aeJYO52ALbGxyTFPw6eGCmPlmuCvbXEoX9gcXatN29E=";
+}
diff --git a/modules/services/nixos/jta/style.css b/modules/services/nixos/jta/style.css
new file mode 100644
index 0000000..1d52d19
--- /dev/null
+++ b/modules/services/nixos/jta/style.css
@@ -0,0 +1,143 @@
+:root {
+ --bg: #0f172a;
+ --card: #1e293b;
+ --accent: #22c55e;
+ --text: #e2e8f0;
+ --text-soft: #cbd5e1;
+ --muted: #94a3b8;
+ --border: #334155;
+
+ --font-body: "Avenir Next", "Segoe UI", "Noto Sans", "Helvetica Neue", sans-serif;
+ --font-heading: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, serif;
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+
+ --step--1: clamp(0.88rem, 0.86rem + 0.12vw, 0.96rem);
+ --step-0: clamp(1rem, 0.98rem + 0.16vw, 1.08rem);
+ --step-1: clamp(1.14rem, 1.08rem + 0.26vw, 1.28rem);
+ --step-2: clamp(1.3rem, 1.2rem + 0.45vw, 1.56rem);
+ --step-3: clamp(1.7rem, 1.45rem + 1.1vw, 2.25rem);
+}
+
+/* Base layout */
+body {
+ font-family: var(--font-body);
+ font-size: var(--step-0);
+ background: var(--bg);
+ color: var(--text);
+ max-width: 850px;
+ margin: 2rem auto;
+ padding: 1.5rem;
+ line-height: 1.7;
+ text-rendering: optimizeLegibility;
+}
+
+/* Headings */
+h1 {
+ font-family: var(--font-heading);
+ font-size: var(--step-3);
+ line-height: 1.15;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ text-align: center;
+ margin: 0 0 1.25rem;
+}
+
+h2 {
+ font-family: var(--font-heading);
+ margin-top: 2.25rem;
+ margin-bottom: 0.75rem;
+ font-size: var(--step-2);
+ line-height: 1.25;
+ letter-spacing: -0.01em;
+}
+
+h3 {
+ font-family: var(--font-heading);
+ margin-top: 1.5rem;
+ margin-bottom: 0.6rem;
+ font-size: var(--step-1);
+ line-height: 1.3;
+ color: var(--text-soft);
+}
+
+/* Text */
+p {
+ color: var(--text);
+ margin-bottom: 1rem;
+ max-width: 65ch;
+}
+
+/* Ordered list (questions) */
+ol {
+ padding-left: 1.2rem;
+ max-width: 65ch;
+}
+
+/* Answer list */
+ul {
+ list-style: none;
+ padding-left: 1.2rem;
+ max-width: 65ch;
+}
+
+ul li {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.6rem;
+ padding: 0.35rem 0.4rem;
+ border-radius: 8px;
+ transition: background 0.15s ease;
+}
+
+/* Checkbox styling */
+input[type="checkbox"] {
+ accent-color: var(--accent);
+ transform: scale(1.2);
+ margin-top: 0.2rem;
+ pointer-events: none;
+}
+
+/* Correct answers */
+li:has(input[type="checkbox"]:checked) {
+ color: #86efac;
+ font-weight: 500;
+}
+
+/* Code blocks */
+pre {
+ font-family: var(--font-mono);
+ font-size: var(--step--1);
+ line-height: 1.6;
+ background: #020617;
+ padding: 1rem;
+ border-radius: 10px;
+ overflow-x: auto;
+ margin: 1rem 0;
+ max-width: 65ch;
+}
+
+code {
+ font-family: var(--font-mono);
+ font-size: 0.95em;
+ background: #020617;
+ padding: 2px 6px;
+ border-radius: 6px;
+}
+
+/* Links */
+a {
+ color: #93c5fd;
+ text-decoration-line: underline;
+ text-decoration-thickness: 2px;
+ text-underline-offset: 0.14em;
+ text-decoration-color: rgba(147, 197, 253, 0.45);
+}
+
+a:hover {
+ text-decoration-color: currentColor;
+}
+
+/* Optional: make everything feel tighter and nicer */
+* {
+ box-sizing: border-box;
+}
diff --git a/modules/services/nixos/openssh.nix b/modules/services/nixos/openssh.nix
index 2d0c45c..1ce1abc 100644
--- a/modules/services/nixos/openssh.nix
+++ b/modules/services/nixos/openssh.nix
@@ -18,7 +18,7 @@ in {
services.openssh = {
enable = true;
- allowSFTP = false;
+ allowSFTP = true;
hostKeys = [
{
diff --git a/modules/services/options.nix b/modules/services/options.nix
index 0484b6b..bb43180 100644
--- a/modules/services/options.nix
+++ b/modules/services/options.nix
@@ -109,6 +109,11 @@ in {
default_port = 8076;
};
+ jta = webserviceOptions {
+ service_name = "jta";
+ default_port = 8072;
+ };
+
copyparty =
(webserviceOptions {
service_name = "copyparty";
diff --git a/modules/terminal/hjem/programs/broot.nix b/modules/terminal/hjem/programs/broot.nix
index d382cbe..8678551 100644
--- a/modules/terminal/hjem/programs/broot.nix
+++ b/modules/terminal/hjem/programs/broot.nix
@@ -13,12 +13,20 @@
default_flags = "-g";
icon_theme = "nerdfont";
- special_paths."~/.config".show = "always";
+ special_paths = {
+ "~/ganymede" = {
+ show = "never";
+ list = "never";
+ sum = "never";
+ };
+ "~/.config".show = "always";
+ };
verbs = [
{
- name = "open-code";
+ name = "edit";
key = "enter";
+ invocation = "e";
execution = "$EDITOR +{line} {file}";
working_dir = "{root}";
apply_to = "file";
@@ -27,17 +35,22 @@
{
name = "lazygit";
key = "ctrl-g";
+ invocation = "lazygit";
execution = "lazygit";
working_dir = "{root}";
- apply_to = "any";
+ leave_broot = false;
+ }
+ {
+ invocation = "opencode {extraargs}";
+ execution = "opencode {extraargs}";
+ working_dir = "{root}";
leave_broot = false;
}
{
name = "justfile";
key = "ctrl-j";
- execution = "${pkgs.just}/bin/just --choose";
+ execution = ''just --choose --chooser "fzf --height=25% --color=bg:-1 --preview 'just --show {}'"'';
working_dir = "{root}";
- apply_to = "any";
leave_broot = false;
}
{
@@ -48,7 +61,15 @@
leave_broot = false;
}
{
- name = "home";
+ key = "alt-enter";
+ execution = "swaymsg exec -- foot -D '{root}' fish -c 'br'";
+ leave_broot = false;
+ }
+ {
+ key = "ctrl-a";
+ internal = "toggle_stage";
+ }
+ {
key = "ctrl-h";
cmd = ":input_clear;:focus ~";
}