Submodule Architecture
Git submodule infrastructure for modular Arcturus-Prime development — how modules and services live in their own repos
Submodule Architecture
Arcturus-Prime uses git submodules to keep modules and backend services in their own repositories while still building as a single Astro site. Module source files live in submodule repos under modules/ and services/, and a prebuild script copies them into the Astro source tree before every dev/build.
Why Submodules
- Independent version control — Each module has its own commit history, branches, and releases
- Focused PRs — Changes to the pentest suite don’t touch the main Arcturus-Prime repo
- Selective deployment — Deploy a daemon update without touching the frontend
- CF Pages compatible — Cloudflare Pages auto-clones submodules during build
Directory Layout
~/Development/Arcturus-Prime/
├── modules/ # Astro UI modules (submodules)
│ ├── pentest/ # → github.com/inovinlabs/Arcturus-Prime-module-pentest
│ └── api-dashboard/ # → github.com/inovinlabs/Arcturus-Prime-module-api-dashboard
├── services/ # Backend services (submodules)
│ ├── net-scanner/ # → github.com/inovinlabs/Arcturus-Prime-net-scanner
│ ├── lab-engine/ # → github.com/InovinLabs/Arcturus-Prime-lab-engine
│ ├── command-center/ # → github.com/KeyArgo/Arcturus-Prime-command-center
│ ├── failover/ # → github.com/inovinlabs/Arcturus-Prime-failover
│ └── workbench/ # → github.com/inovinlabs/Arcturus-Prime-workbench
├── scripts/
│ └── install-modules.sh # Copies module files into src/ tree
├── src/ # Module files land here (gitignored)
└── .gitmodules # 7 submodule definitions
Two Categories
Modules (modules/)
Astro UI modules that own pages, API routes, components, and config files. Each module has an install.sh that copies files into the Arcturus-Prime source tree.
| Module | Files | What It Copies |
|---|---|---|
| pentest | 18 | 8 pages, 6 API routes, 1 component, 3 configs |
| api-dashboard | 5 | 1 page, 2 API routes, 1 config, 1 JSON config |
Services (services/)
Backend services that run independently (FastAPI, Flask, shell scripts). They don’t have install.sh — they’re included as submodules for co-location and convenience, not for the Astro build.
| Service | Language | Purpose |
|---|---|---|
| net-scanner | Python/FastAPI | Network device scanning (Sonar) |
| lab-engine | Python/FastAPI | Proxmox lab provisioning |
| command-center | Python/Flask | Infrastructure status dashboard |
| failover | Shell | Failover monitoring scripts |
| workbench | Python | Dispatch/orchestration tools |
How install-modules.sh Works
# scripts/install-modules.sh
ARGOBOX_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export ARGOBOX_ROOT
for mod_dir in "$ARGOBOX_ROOT"/modules/*/; do
if [[ -f "$mod_dir/install.sh" ]]; then
bash "$mod_dir/install.sh"
fi
done
Each module’s install.sh copies files from the submodule into the expected Astro paths:
# modules/pentest/install.sh (simplified)
cp "$SCRIPT_DIR"/src/pages/admin/pentest/*.astro "$Arcturus-Prime/src/pages/admin/pentest/"
cp "$SCRIPT_DIR"/src/pages/api/admin/pentest/*.ts "$Arcturus-Prime/src/pages/api/admin/pentest/"
cp "$SCRIPT_DIR"/src/components/pentest/*.astro "$Arcturus-Prime/src/components/pentest/"
cp "$SCRIPT_DIR/src/config/modules/pentest.ts" "$Arcturus-Prime/src/config/modules/pentest.ts"
Why copies, not symlinks? Astro/Vite doesn’t follow symlinks for relative imports. The existing Arcturus-Prime-module-api-dashboard discovered this — its install.sh has a comment explaining the constraint.
Build Integration
install-modules.sh runs automatically via npm hooks:
{
"predev": "bash scripts/install-modules.sh && ...",
"prebuild": "bash scripts/install-modules.sh && ..."
}
- Local dev:
npm run dev→ predev runs install-modules.sh → files copied → Astro dev server starts - CF Pages build:
npm run build→ prebuild runs install-modules.sh → files copied → Astro builds
.gitignore Strategy
Module-installed files are gitignored in Arcturus-Prime so they’re only tracked in their own repos:
# Pentest module
src/pages/admin/pentest/
src/pages/api/admin/pentest/
src/components/pentest/
src/config/modules/pentest.ts
src/config/pentest-nodes.ts
src/config/pentest-targets.ts
# API Dashboard module
src/pages/admin/api-dashboard.astro
src/pages/admin/_api-dashboard-config.json
src/config/modules/api-dashboard.ts
src/pages/api/admin/api-usage.ts
src/pages/api/admin/api-dashboard-providers.ts
CF Pages & Submodule URLs
Submodule URLs in .gitmodules point to GitHub (not Gitea) because:
- CF Pages clones from GitHub and needs public access to submodule URLs
- Gitea (git.Arcturus-Prime.com) requires authentication for git HTTP operations
- All submodule repos are mirrored to both Gitea and GitHub
For local development, the GitHub URLs work fine. To use Gitea locally instead, override with:
git config submodule.modules/pentest.url https://git.Arcturus-Prime.com/InovinLabs/Arcturus-Prime-module-pentest.git
Day-to-Day Workflow
Editing a module
- Edit files in
modules/pentest/src/... npm run devauto-runs install-modules.sh (copies files tosrc/)- See changes in browser at localhost:4321
- Commit and push in
modules/pentest/:cd modules/pentest && git add -A && git commit -m "update" && git push - Optionally bump the submodule ref in Arcturus-Prime:
cd ~/Development/Arcturus-Prime git add modules/pentest && git commit -m "bump pentest submodule"
After a fresh clone
git clone --recurse-submodules https://github.com/inovinlabs/Arcturus-Prime.git
cd Arcturus-Prime
npm install
npm run dev # install-modules.sh runs automatically
Or if you forgot --recurse-submodules:
git submodule update --init --recursive
Adding a new module
- Create
Arcturus-Prime-module-<name>repo on GitHub - Mirror module files, matching Arcturus-Prime’s path structure
- Add
install.shanduninstall.sh git submodule add <url> modules/<name>git rm --cachedthe Arcturus-Prime copies of those files- Add the file paths to
.gitignore - Verify: delete files, run
install-modules.sh,npm run dev
Repos
| Repo | GitHub | Gitea | Type |
|---|---|---|---|
| Arcturus-Prime-module-pentest | inovinlabs/Arcturus-Prime-module-pentest | InovinLabs/Arcturus-Prime-module-pentest | Module |
| Arcturus-Prime-module-api-dashboard | inovinlabs/Arcturus-Prime-module-api-dashboard | InovinLabs/Arcturus-Prime-module-api-dashboard | Module |
| Arcturus-Prime-net-scanner | inovinlabs/Arcturus-Prime-net-scanner | InovinLabs/Arcturus-Prime-net-scanner | Service |
| Arcturus-Prime-lab-engine | InovinLabs/Arcturus-Prime-lab-engine | InovinLabs/Arcturus-Prime-lab-engine | Service |
| Arcturus-Prime-command-center | KeyArgo/Arcturus-Prime-command-center | KeyArgo/Arcturus-Prime-command-center | Service |
| Arcturus-Prime-failover | inovinlabs/Arcturus-Prime-failover | InovinLabs/Arcturus-Prime-failover | Service |
| Arcturus-Prime-workbench | inovinlabs/Arcturus-Prime-workbench | InovinLabs/Arcturus-Prime-workbench | Service |
Remaining Extractions
The 2 extracted modules (pentest, api-dashboard) serve as the template. Approximately 26 more modules can be extracted following the same pattern — each gets its own repo, install.sh, and gitignore entries. Priority and timeline TBD.
Gotchas
((var++))is falsy when var=0 — Usevar=$((var + 1))in bash scripts withset -e- Astro/Vite symlink limitation — Must use file copies, not symlinks
- Gitea requires auth for git HTTP — Use GitHub URLs in .gitmodules for CF Pages
- Module discovery still works —
import.meta.glob('./modules/*.ts')finds copied config files insrc/config/modules/ - Shared components stay in Arcturus-Prime — VNCEmbed, TerminalEmbed, etc. aren’t extracted (used by multiple modules)