Skip to main content
Site Blueprint

Submodule Architecture

Git submodule infrastructure for modular Arcturus-Prime development — how modules and services live in their own repos

February 27, 2026

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.

ModuleFilesWhat It Copies
pentest188 pages, 6 API routes, 1 component, 3 configs
api-dashboard51 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.

ServiceLanguagePurpose
net-scannerPython/FastAPINetwork device scanning (Sonar)
lab-enginePython/FastAPIProxmox lab provisioning
command-centerPython/FlaskInfrastructure status dashboard
failoverShellFailover monitoring scripts
workbenchPythonDispatch/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

  1. Edit files in modules/pentest/src/...
  2. npm run dev auto-runs install-modules.sh (copies files to src/)
  3. See changes in browser at localhost:4321
  4. Commit and push in modules/pentest/:
    cd modules/pentest && git add -A && git commit -m "update" && git push
  5. 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

  1. Create Arcturus-Prime-module-<name> repo on GitHub
  2. Mirror module files, matching Arcturus-Prime’s path structure
  3. Add install.sh and uninstall.sh
  4. git submodule add <url> modules/<name>
  5. git rm --cached the Arcturus-Prime copies of those files
  6. Add the file paths to .gitignore
  7. Verify: delete files, run install-modules.sh, npm run dev

Repos

RepoGitHubGiteaType
Arcturus-Prime-module-pentestinovinlabs/Arcturus-Prime-module-pentestInovinLabs/Arcturus-Prime-module-pentestModule
Arcturus-Prime-module-api-dashboardinovinlabs/Arcturus-Prime-module-api-dashboardInovinLabs/Arcturus-Prime-module-api-dashboardModule
Arcturus-Prime-net-scannerinovinlabs/Arcturus-Prime-net-scannerInovinLabs/Arcturus-Prime-net-scannerService
Arcturus-Prime-lab-engineInovinLabs/Arcturus-Prime-lab-engineInovinLabs/Arcturus-Prime-lab-engineService
Arcturus-Prime-command-centerKeyArgo/Arcturus-Prime-command-centerKeyArgo/Arcturus-Prime-command-centerService
Arcturus-Prime-failoverinovinlabs/Arcturus-Prime-failoverInovinLabs/Arcturus-Prime-failoverService
Arcturus-Prime-workbenchinovinlabs/Arcturus-Prime-workbenchInovinLabs/Arcturus-Prime-workbenchService

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 — Use var=$((var + 1)) in bash scripts with set -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 worksimport.meta.glob('./modules/*.ts') finds copied config files in src/config/modules/
  • Shared components stay in Arcturus-Prime — VNCEmbed, TerminalEmbed, etc. aren’t extracted (used by multiple modules)
architecturemodulessubmodulesgitinfrastructure