ENDERU
GitHub22 repositoriesDaily Tech3 subscribersaudion.devthe product shelfRSSrelease feed
← All notesProjects

Audion Hub Manager — how it is built

Русский · About · Console command · Decisions

Contents

The parts

Start.exe ──▶ App\AudionHubManager.exe  (the window: C#, Avalonia, .NET 10 inside the folder)
                 │                                  │
                 │ git — by itself, by the canon    │ hub_cli.cmd — one JSON per command
                 ▼                                  ▼
          the repository of the chosen layer   system_core\cli.py → system_core\core\*  (Python 3.12 of runtime\)

The window is the face of the console command: the registry, the mirror, the checks, the batches, the lab and Forgejo go through hub_cli.cmd, and the same can be done without the window. Git the window calls by itself: the command it shows is the lesson, and the state of a repository (status, graph, branches) is read often.

The window

WhereWhat
Source\MainWindow.*.csthe sections: Changes, History, Branches, Sync, Mirror, Projects, Service; the header and the layer — Project, the column of projects — Side, commit the marked — Marked, the folder panel — Folders, the terminal — Command, Run
Source\Views\FadeBox.csa text or a row that does not fit fades out over its last 16 px instead of an ellipsis
Source\Core\Git.csquiet reading for drawing: status --porcelain=v2 -b, log --format, for-each-ref
Source\Core\Canon.csthe canon from hub_cli canon: captions, tips, risk
Source\Core\Hub.cscalling hub_cli.cmd and reading its JSON; a stdin line (a token) goes only into the process
Source\Core\Runner.cs, ChildJob.csrunning commands with live output; a Windows job: STOP and closing the window end the whole tree of processes
Source\Views\GraphCell.csthe graph of commits: branch lanes by parents
Source\App.axamlfour themes and the colours of Git: in the index, changed, new, deleted, conflict, renamed, outside the profile
Source\Ps\the program's PowerShell 7, App\AudionPs.exe (Microsoft.PowerShell.SDK)

Every Git command of a button runs with this environment: GIT_TERMINAL_PROMPT=0 (nothing waits for a password), GIT_EDITOR=true and GIT_SEQUENCE_EDITOR=true (an editor the window could not show is not opened), the colours through GIT_CONFIG_COUNT (color.ui=always, core.quotepath=false), so the command shown stays the one typed by hand. Git's messages on the error stream are not errors: the exit code tells the outcome.

Dangerous operations ask with the whole command, what is not committed and how to undo it. A line typed by hand is split into its Git calls — the GIT mode, or every git between &&, ||, ;, | and line breaks of PS and CMD — and each is judged by its arguments in any order (Source\Core\TypedCommand.cs): reset --hard and the other modes of reset, commit --amend, restore of the working folder, checkout --, switch -f, clean -f, push with force, deletion, mirror or a +/: refspec, branch -D, stash drop, rebase, filter-branch, reflog expire, gc --prune, update-ref -d, rm without --cached, tag -d. What has an entry of the canon asks with its words, reset --hard with the typed RESET; recursive deletion of PS and CMD is caught by a filter of words.

The build: install\Build-App.cmd — the tests of the engine and the canon, publishing the PowerShell and the window (win-x64, with .NET), then the self-check of the built window (--self-test --just-built): the console command answers, the canon reads and every operation has its words in both languages, the theme has the colours of Git, the number of the exe is that of config\version.json. The build trusts only the line [OK] all checks passed of the report.

The Git canon

system_core\core\git_canon.py — 63 entries. The fields of an entry: id, the command with places ({path}, {commit}, {branch}, {remote}, {message}, {tag}, {paths} — a list), risk, mode, the window's section, the Russian and English words "what it does" and "how to undo it". The caption of a button (short) is the command without its places and quiet flags: git restore --staged, git switch -c.

RiskMode
readchanges nothingrunat once
localchanges this repositoryaskafter a question
nettalks to a servertemplateinto the terminal's line, to be finished
dangercan lose work

The canon is read by the window (hub_cli canon), the tests (tests\test_git_canon.py runs every entry on a fresh copy of the lab) and the documents (hub_cli canon --docs writes the tables between <!-- canon:… --> and <!-- /canon -->; tests\test_canon_docs.py fails when a table falls behind). The program has no other Git command.

The engine

Module of system_core\core\What
hub_ops.pyregistry and layers, the profile check of paths, batches, workspace, addresses and remotes.json, the lab in the registry, the machine's tools, the Recycle Bin
project_registry.py, project_importer.pyreading the registry; finding project roots in a folder and merging them in
projection_engine.py, scan_engine.py, mask_engine.py, copy_engine.pythe mirror: the walk by profile, masks, copying, BLAKE3 sums, reports
docs_view.pythe DOCS layer: the Markdown and texts of the SOURCE by the profile markdown_only_projection
safety.pythe scan for secrets and heavy files
storage_layout.pythe storage roots and their roles
git_engine.pygit status for batches, version tags of a series, the check of a remote address
git_credentials.pythe git credential protocol: fill, approve, reject
forgejo_api.py, forgejo_service.pythe Forgejo and Gitea API, sign-in by token, repositories
auth_doctor.pythe access check: Git, name and e-mail, gh, glab, SSH, remote addresses
external_apps.pythe path of this machine's VS Code
output_decode.pydecoding output (UTF-8, OEM, Cyrillic)
lab.py, git_canon.py, canon_docs.pythe lab, the canon, the canon tables of the documents

The registry and the layers

<root of the projects>\hub_projects.json (project_registry.registry_path): each root has a registry of its own. active_project_id and projects — records with id, title, source_path, projection_path, docs_path, profile, default_branch, notes. A relative path counts from the folder of the registry; paths inside the root are written from it, the others in full, and the root moves together with its registry. A record without a GIT COPY gets Hub Data\<title> beside the program — as a scan gives it. A broken file does not stop the start: the surviving records are read, the answer names the trouble (load_error). The registry is this machine's data and does not go into git. When it is missing the program makes it: from config\projects.json when the registry lies there (the paths are rebased from the program folder to the root, the old file is removed); for the shipped root from config\projects.example.json; for a root of one's own empty.

projects export FILE writes the registry into a file with full paths; projects import FILE rebases the paths of the file from where it lies to the root and adds the projects the registry does not have (--replace takes the file's list whole).

Each layer (source, mirror, docs) has a repository state: own, parent (a folder inside another repository: the commands would go there), none.

The mirror

walk the SOURCE by the profile → walk the GIT COPY → compare the paths
1 create the allowed folders
2 copy and update files
3 set the modification times
4 if there were no errors and no differences — delete what is extra in the GIT COPY
5 remove folders left empty
6 markers of empty folders — only where a profile asks (`preserve_empty_dirs`); the profiles shipped put none

The SOURCE never changes; .git/** is protected from the walk, deletion and upkeep. A plan is checked again before writing: a stale or edited plan deletes nothing outside its area. Neither walk follows a symbolic link or a junction (lstat, the reparse tag): such a folder or file is skipped, and every write, copy and delete resolves its path and refuses one that leads out of its layer. A folder or file a walk could not read becomes an error item of the plan (source_unreadable, mirror_unreadable), so step 4 deletes nothing; verification counts it as an error. Reports — logs\<project>\YYYYMMDD_HHMMSS_microseconds_kind.json.

CompareHow
quicksize and time
safe, safe_blake3sums only when the size is the same and the time differs
strict, strict_blake3, metadata_then_blake3sums for every file of the same size, even with the same time; metadata_then_blake3 is the default

Sums carry their algorithm: blake3:…; sha256:… is the fallback where BLAKE3 is missing. Any apply with errors or differences returns exit code 1.

Profiles

config\projection_profiles.json:

ProfileWhat
audion_python_project_projectiona project of any kind; 476 include masks, 243 exclusions, 89 folders not walked
audion_python_project_projection_strictextends the project profile (extends), the strict check
markdown_only_projectionMarkdown and texts only — the DOCS layer

The project profile lets through the sources, documents and settings of the common languages and build systems: Python, the web (JS, TS, frameworks), C and C++, .NET, the JVM, Go, Rust, Swift, Dart and Flutter, Ruby, PHP, Elixir, Haskell, R, Julia, Lua, shells and PowerShell, SQL, Terraform, Nix, Docker and Kubernetes, CMake, Bazel, Meson, Qt (forms, resources, QML), WiX, Inno Setup and NSIS installers, translations, templates, diagrams as text; dependency lock files, CI (GitHub, GitLab, Forgejo, Gitea and others), linter and formatter settings, the shared .vscode\tasks.json, launch.json, extensions.json, .devcontainer; the text files of Unity and Godot. Pictures and fonts of the sources pass up to a megabyte. It keeps out dependencies and caches (node_modules, .venv, target, obj, .gradle, .next and dozens more), builds and packages, archives and disk images, media and the files of design programs, databases, data sets and trained models, logs and temporary files, .env, private keys, keys and tokens kept in text files (api_key_*.txt, token.json), password stores, the state of Terraform, machine-local *.local.* and *.user; for Audion programs, the built window App\, its state, the licence pack and the release manifest.

The project's own .gitignore stands over the profile: what it hides the mirror neither walks nor copies (respect_gitignore). A project with a repository of its own is asked as it is — what it has committed is never hidden; any other folder through a throwaway git folder, so that only its own .gitignore counts, not the one of a repository it lies in. Hidden folders of the first two levels are cut off before the walk: a build tree in build-ui\ is not walked at all.

Hundreds of masks are checked in a fraction of a second: exact names by a set, endings like *.py by one check of the ending, the rest by one compiled expression (mask_engine._matcher); the answer is that of the masks one by one (tests\test_mask_engine.py).

Key of a profileWhat it does
dry_run_default: truean apply without --apply only shows
require_include_filter: true, min_include_globswith no masks the plan fails
allow_unfiltered_fullthe only way to allow a full copy; no profile the program ships sets it
mirror_scope: "filtered"the GIT COPY is brought to the SOURCE only by the masks
compare_modethe way of comparing; the profiles shipped use strict_blake3
small_include_globs, small_include_max_file_bytessmall files that pass up to their limit (pictures and fonts — up to 1 MB)
hide_dirsfolders the walk never enters
exclude_dir_contentsfolders that stay empty with a marker: service folders, builds, outputs
extendsa profile takes another and changes its own keys
preserve_empty_dirs, marker_filekeep empty folders by a marker; off in the profiles shipped — a program's folders are made by its install\init_folders.cmd
respect_gitignoredo not copy what the project's .gitignore hides; on by default
output_dirsfolder guesses: builds and dependencies in most projects, sources in some (app\ of Next.js, build\ of Electron, bin\ of scripts, vendor\ of Go); kept out only when the project has no .gitignore of its own
extensionless_text_bytesa file without an extension the list does not name (install, UNLICENSE, the hook .husky\pre-commit) passes when it is within the limit and text: no zero byte in its first 8 KB

A folder rule is a name anywhere (logs), a path from the root (install/download) or, with a leading /, a name at the root only (/App — the built window, not an app folder of sources deeper down). A name may be a mask: cmake-build-*.

The same profile decides what goes into a commit: hub_cli check-paths before add and commit, a file outside the profile is not put into the index.

Access and Forgejo

The program keeps no passwords, tokens or private keys. Access goes by the usual means: SSH keys and ssh-agent, Git Credential Manager, GitHub CLI, GitLab CLI. The checks never wait for input: GIT_TERMINAL_PROMPT=0, SSH with BatchMode=yes.

Own Forgejo and Gitea servers — config\forgejo_hosts.json: the address, the SSH user and port, the kind of address, the login; no tokens. It is data of the machine, as the registry is: the file does not go into git, the release cleaner removes it. The account is a personal access token with the header Authorization: token …: it is checked by a request to /api/v1/user and handed to Git's credential helper (git credential approve); after that git push over HTTPS takes it by itself. OAuth2 sign-in is not used: Forgejo's OAuth2 has no scopes yet, such a token carries the account's administrative rights, lives briefly and needs a separate mechanism for git push.

Storage

config\storage_layout.json — the roots and their roles; ${APP_ROOT} is the program folder:

RootRoleAs shipped
manager_rootthe program itself${APP_ROOT}
hub_data_rootthe GIT COPIES of projects${APP_ROOT}/../Hub Data
docs_rootthe DOCS layer${APP_ROOT}/../Hub Docs
full_projects_rootthe live projects, the source of truth${APP_ROOT}/..

hub_cli storage checks the roots are there, none lies inside another, the GIT COPY of each project is under hub_data_root and its DOCS under docs_root. DOCS is optional: the docs root is needed only when at least one project keeps the layer. The demo project and the Git lab keep their copies inside the program and are not held to the layout.

The root of the live projects can be chosen for one machine: hub_cli storage --projects-root FOLDER writes it into config\storage_layout.local.json (beside the program it is kept as ${APP_ROOT}/../…, elsewhere in full; inside the GIT COPIES or DOCS it is refused), --reset-projects-root gives the shipped root back. The file is not in git and the release cleaner takes it (*.local.json): a release carries the layout of the program, not of the machine it was made on. A change of the root answers with exit code 0 when the root is written; what of the projects of the new root the layout does not hold is told by off_layout (source, projection, docs — lists of id), and the window names them in a line under the command.

Commit the marked

Source\MainWindow.Marked.cs: a checkpoint of every project marked in the column, by SKILL.md — mirror apply --apply, mirror verify, safety --layer mirror, check-paths --layer mirror --stdin over the changed paths, add and commit of the canon in the GIT COPY. The steps go through the same runner as the buttons; the next step is put in when the one before has ended whole (Done → Dispatcher.Post). A project without a repository of its own in the GIT COPY is passed over, a step that says no stops only its project, STOP the whole chain.

The lab

system_core\core\lab.py builds ten repositories with a clock of its own: the author, the date and the bytes of every file are fixed, so the commit hashes are the same on any machine and in every run. _servers\ holds bare repositories, the "server" of the lesson on remotes.

Checks

WhatHow
engine, console command, canonruntime\python.exe -m pytest -q tests — 172 tests; the canon on a fresh copy of the lab
windowinstall\Build-App.cmd — the self-check of the built window
releaseinstall\verify_portable_env.cmd — the environment (system_core\doctor.py), the console command, Start.exe, the window by its report

The builder

builder_main.cmd — a menu on FZF:

StepWhat
01, 02the Python environment: runtime\ and wheelhouse\ from install\requirements_full.in (CMD or PowerShell)
03system_core\fzf.exe
05the BLAKE3 package
06the program's folders
07the lock file of dependencies
09an offline install from runtime\ and wheelhouse\
70cleaning the install cache and what a build of the window leaves
71the release check
72the encoding of .cmd: UTF-8 without BOM, CRLF
95–98open install\, runtime\, wheelhouse\, licenses\

The window is built apart: install\Build-App.cmd. cleanup_project.cmd brings the project back to its sources: it removes the runtime, caches, logs, the state of the window and what a build leaves; App\ and Start.exe stay.

Documents

Markdown is the source. The canon tables of the documents are written by hub_cli canon --docs, never by hand. The DOCS layer is not checked by BLAKE3: its technical copy is in the GIT COPY already.

Edited 10.24.2026