Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2b5f20f
Use the full Apache-2.0 licence text, add NOTICE
manojdev-ai Aug 2, 2026
0474f9c
Pin dependencies and container versions
manojdev-ai Aug 2, 2026
6eedd94
Add third-party licence inventory and workload image SBOM tooling
manojdev-ai Aug 2, 2026
10f382d
Disclose model licences and forward HF_TOKEN to gated downloads
manojdev-ai Aug 2, 2026
4fdc2b8
Serve demo-ui fonts and Chart.js locally instead of from CDNs
manojdev-ai Aug 2, 2026
332cd77
Pin installers to a commit SHA, document the release process
manojdev-ai Aug 2, 2026
d5297ce
Enforce licence compliance in CI, document the position
manojdev-ai Aug 2, 2026
12ea007
Mark scripts executable in the index, harden CI against flakes
manojdev-ai Aug 2, 2026
363b856
Open source GCR registry
TejaMuraharirao Aug 3, 2026
0e89d9b
SHA added ..2nd issue resolved
TejaMuraharirao Aug 3, 2026
2d3fff2
linked catalog json file and python file to make it one source of tru…
TejaMuraharirao Aug 4, 2026
beee7bc
remove review
manojdev-ai Aug 4, 2026
3680008
versions updated and made it fixed rather than latest
TejaMuraharirao Aug 4, 2026
fd92465
license changed and jupyter changes
TejaMuraharirao Aug 4, 2026
3780a63
char length issue
TejaMuraharirao Aug 4, 2026
87f69d1
changes made according to the new workloads
TejaMuraharirao Aug 4, 2026
77b9217
data path of jupyter configured from nfs
TejaMuraharirao Aug 4, 2026
2f8333d
duplicate revision ID
TejaMuraharirao Aug 5, 2026
d748315
_GCR_LOGIN_CMD was removed
TejaMuraharirao Aug 5, 2026
8ec9b56
update catalog.json image tags to 1.0.0-nvidia
TejaMuraharirao Aug 5, 2026
fa856b6
entrypoint over riding
TejaMuraharirao Aug 5, 2026
d6f8555
added t4 also in the default model configs
TejaMuraharirao Aug 5, 2026
aea419d
chore: add workload image SBOMs (1.0.0-nvidia, 2.3.1-nvidia, 2.2.0-nv…
rsingh-alt Aug 5, 2026
31db5b5
add GPL OS package source offer to NOTICE
TejaMuraharirao Aug 5, 2026
8b8a35e
Merge branch 'license-review' of github.com:corespan/aistudio-server …
TejaMuraharirao Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ RABBITMQ_PORT=5672
RABBITMQ_USERNAME=aistudio
RABBITMQ_PASSWORD=aistudio

# Workload Images (GCP Artifact Registry)
# Full image path: us-docker.pkg.dev/aimlworkbench/workbench-registry/services/workloads/llminference:1.0.0
# Workload Images (GCP Artifact Registry — public read, no auth required on GPU nodes)
# Full image path: us-docker.pkg.dev/aimlworkbench/aistudio/llminference:2.3.1-nvidia
GCP_REGISTRY_URL=us-docker.pkg.dev
GCP_PROJECT_ID=aimlworkbench
GCP_REPOSITORY=workbench-registry
GCP_IMAGE_PATH=services/workloads
WORKLOAD_IMAGE_TAG=2.3.0-nvidia
GCP_REPOSITORY=aistudio
WORKLOAD_IMAGE_TAG=2.3.1-nvidia
JUPYTER_IMAGE_TAG=2.2.0-nvidia

# Model Storage — how your GPU node accesses model weights
Expand All @@ -32,6 +31,46 @@ MODEL_STORAGE_MODE=huggingface
MODEL_LOCAL_PATH=/home/ubuntu/models
# MODEL_GCS_BUCKET=gs://your-bucket

# Hugging Face access token — required for GATED models
#
# Several models in catalog.json (all the Meta Llama ones) are gated: the
# publisher requires you to request access on huggingface.co and accept their
# licence before the weights can be downloaded. Without a token from an approved
# account, those runs fail with HTTP 401 unless the weights are already in the
# mounted cache.
#
# Set this on the GPU NODE, not here — the worker forwards it into the workload
# container, so the credential never transits the API or gets written into a
# run manifest. Put it in ~/.aistudio/env on each node:
#
# ssh <gpu-node> 'mkdir -p ~/.aistudio && \
# echo "HF_TOKEN=hf_xxxx" > ~/.aistudio/env && chmod 600 ~/.aistudio/env'
#
# Do NOT append `export HF_TOKEN=...` to ~/.bashrc. Debian and Ubuntu ship a
# ~/.bashrc that returns early for non-interactive shells, and every command
# this server runs over SSH is non-interactive — so the variable would be set
# when you log in by hand and unset for every benchmark. The dedicated file is
# sourced explicitly by the worker and sidesteps that entirely.
#
# Verify: make check-node-env NODE=<gpu-node>
# Check which models need it: python3 scripts/check_model_access.py
# Licence terms per model: MODEL-LICENSES.md
#
# A token is not a licence. You are responsible for accepting each model's
# terms on your own Hugging Face account. CoreSpan cannot grant access.

# GPU node paths
# NODE_RESULTS_PATH — where benchmark output is written on the GPU node.
# Each run creates /results/<run_id>/ containing benchmark_result.json, summary.json,
# and logs/status.json. The HuggingFace model cache (~/.cache/huggingface) is
# mounted automatically — no separate data path config needed.
NODE_RESULTS_PATH=/results

# NODE_JUPYTER_DATA_PATH — where Jupyter notebooks are stored on the GPU node.
# Each session creates /data/<workload_id>/ inside the Jupyter container.
# Can be any writable local directory — does not need to be shared storage.
NODE_JUPYTER_DATA_PATH=/data

# SSH — how Celery connects to your GPU node
SSH_KEY_PATH=~/.ssh/id_rsa
SSH_DEFAULT_USER=ubuntu
Expand Down
233 changes: 233 additions & 0 deletions .github/workflows/compliance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
name: Licence compliance

# Makes the licence position self-enforcing. Every finding fixed by hand once
# has a check here so it cannot silently regress.

on:
push:
branches: [master, main]
pull_request:
schedule:
# Weekly. Model gate status and upstream licences change without warning.
- cron: "0 6 * * 1"
workflow_dispatch:

permissions:
contents: read

jobs:
licence-files:
name: Licence files present and detectable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: LICENSE is the full Apache-2.0 text, not the header boilerplate
run: |
# The 17-line notice meant for source-file headers is ~740 bytes and
# gets classified NOASSERTION by every scanner, which enterprise
# licence tooling treats as unknown-licence and blocks on.
size=$(wc -c < LICENSE)
echo "LICENSE is $size bytes"
if [ "$size" -lt 10000 ]; then
echo "::error file=LICENSE::LICENSE is $size bytes — too short to be the full Apache-2.0 text (~11 KB). Looks like the header boilerplate was pasted instead."
exit 1
fi
for section in \
"2. Grant of Copyright License" \
"3. Grant of Patent License" \
"5. Submission of Contributions" \
"6. Trademarks" \
"END OF TERMS AND CONDITIONS"
do
grep -qF "$section" LICENSE || {
echo "::error file=LICENSE::missing section: $section"; exit 1; }
done

- name: Required compliance files exist
run: |
missing=0
for f in NOTICE THIRD-PARTY-NOTICES.md MODEL-LICENSES.md demo-ui/vendor/NOTICE; do
[ -f "$f" ] || { echo "::error::missing $f"; missing=1; }
done
exit $missing

- name: Scripts with a shebang are executable
run: |
# This repo has core.fileMode=false (it is developed on Windows), so
# chmod +x in a working tree is invisible to git and every script was
# committed 100644. A CI runner then checks them out non-executable
# and `./scripts/foo.sh` dies with exit 126 — which is how this check
# came to exist.
#
# The mode has to be set in the index directly:
# git update-index --chmod=+x <file>
failed=0
while IFS= read -r path; do
head -c2 "$path" 2>/dev/null | grep -q '^#!' || continue
[ -x "$path" ] && continue
echo "::error file=$path::has a shebang but is not executable. Fix with: git update-index --chmod=+x $path"
failed=1
done < <(git ls-files)
[ "$failed" -eq 0 ] && echo "All shebang scripts are executable."
exit $failed

- name: GitHub classifies the repo licence as Apache-2.0
# Only meaningful on the default branch of the real repo; the API reads
# the pushed state, not the working tree.
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
env:
GH_TOKEN: ${{ github.token }}
run: |
spdx=$(gh api "repos/${{ github.repository }}/license" --jq '.license.spdx_id' 2>/dev/null || echo "NONE")
echo "GitHub reports: $spdx"
if [ "$spdx" != "Apache-2.0" ]; then
echo "::error::GitHub licence detection returned '$spdx', expected Apache-2.0. Enterprise scanners will treat this as unknown-licence."
exit 1
fi

dependencies:
name: Dependencies pinned and inventoried
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: requirements.txt is fully pinned
run: |
# A `>=` range means two installs a month apart resolve to different
# transitive sets, with different licences, and neither the SBOM nor
# the benchmark numbers describe what actually ran.
#
# Matches any requirement line (non-comment, non-indented — uv puts the
# "# via" provenance on indented lines) carrying a range operator
# rather than ==. Deliberately not a package-name character class:
# requirements.in contains `uvicorn[standard]>=0.30.0` and
# `sqlalchemy[asyncio]>=2.0.0`, and a class that fails to account for
# the brackets would let exactly those through.
if grep -nE '^[^#[:space:]].*(>=|<=|~=|!=|<|>|\*)' requirements.txt; then
echo "::error file=requirements.txt::unpinned requirement found. Edit requirements.in and run 'make deps-lock'."
exit 1
fi
if ! grep -qE '^[^#[:space:]].*==' requirements.txt; then
echo "::error file=requirements.txt::no pinned entries found at all."
exit 1
fi
echo "All requirements pinned ($(grep -cE '^[^#[:space:]]+==' requirements.txt) packages)."

- name: requirements.txt is a valid resolution of requirements.in
run: |
pipx install uv || pip install uv

# Seed the output with the committed pins before recompiling.
#
# uv preserves existing pins in the output file unless --upgrade is
# passed. Compiling into an empty scratch file instead would resolve
# everything to latest, so this check would fail the moment any of the
# 52 packages cut a release — turning an unrelated PR red for a reason
# its author cannot act on.
#
# What we want to assert is "requirements.txt satisfies
# requirements.in", not "requirements.txt is the newest possible
# resolution". Deliberate upgrades go through `make deps-lock`.
cp requirements.txt /tmp/recompiled.txt
uv pip compile requirements.in --python-version 3.11 -o /tmp/recompiled.txt --quiet

# Ignore comments: the header records the output filename, which differs.
if ! diff <(grep -v '^#' requirements.txt) <(grep -v '^#' /tmp/recompiled.txt); then
echo "::error file=requirements.txt::not a valid resolution of requirements.in. Run 'make deps-lock' and commit."
exit 1
fi
echo "requirements.txt satisfies requirements.in."

- name: THIRD-PARTY-NOTICES.md matches the pinned set
run: python3 scripts/generate_third_party_notices.py --check

frontend-assets:
name: demo-ui makes no third-party requests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: No CDN or Google Fonts references
run: |
# Two failure modes this prevents: GDPR exposure from disclosing
# visitor IPs to Google (LG München I, 3 O 17493/20), and a broken
# dashboard in air-gapped clusters, which is the target deployment.
if grep -rnE 'src=["'"'"']https?://|href=["'"'"']https?://(fonts|cdn)\.' demo-ui/ --include='*.html' --include='*.js' --include='*.css'; then
echo "::error::demo-ui references a third-party origin. Vendor the asset with scripts/vendor_frontend_assets.sh."
exit 1
fi
echo "No third-party asset references."

- name: Committed licence texts match a clean regeneration
run: |
# The binaries (.woff2, chart.umd.js) are gitignored and fetched at
# setup time, so they are not in the checkout to compare. What must
# match are the compliance artifacts that ARE committed: the generated
# CSS, the NOTICE, and the upstream licence texts. If those drift from
# what the declared versions actually produce, the notices no longer
# describe the fonts users receive.
VENDOR_DEST=/tmp/vendor-check ./scripts/vendor_frontend_assets.sh

failed=0
for f in fonts.css NOTICE fonts/LICENSE-Inter-OFL.txt \
fonts/LICENSE-JetBrainsMono-OFL.txt js/LICENSE-chartjs.md; do
if ! diff "/tmp/vendor-check/$f" "demo-ui/vendor/$f"; then
echo "::error file=demo-ui/vendor/$f::differs from a clean regeneration"
failed=1
fi
done
[ "$failed" -eq 0 ] || {
echo "Run ./scripts/vendor_frontend_assets.sh and commit the result."
exit 1
}

- name: Every font referenced by fonts.css is actually produced
run: |
# fonts.css is committed but the .woff2 files it points at are not.
# Verify the generated pair is self-consistent, so a weight added to
# the script's list can never ship as a CSS rule with no file behind it.
missing=0
for url in $(grep -oE 'url\("\./[^"]+"\)' demo-ui/vendor/fonts.css \
| sed -E 's|url\("\./||; s|"\)||'); do
[ -f "/tmp/vendor-check/$url" ] || { echo "::error::fonts.css references missing $url"; missing=1; }
done
exit $missing

models:
name: Model gate status and licences
runs-on: ubuntu-latest
# Informational: the Hub can rate-limit or be briefly unavailable, and that
# should not block a merge. Read the log on the weekly run.
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: python3 scripts/check_model_access.py

images:
name: Workload image SBOMs
runs-on: ubuntu-latest
# Needs registry credentials. Enable once the GCP workload-identity
# federation binding for this repo exists — see RELEASE.md.
if: false
steps:
- uses: actions/checkout@v4
- uses: anchore/sbom-action/download-syft@v0
- name: Authenticate to Artifact Registry
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
service_account: ${{ secrets.GCP_SA_EMAIL }}
- run: gcloud auth configure-docker us-docker.pkg.dev --quiet
- run: ./scripts/generate_sbom.sh
- uses: actions/upload-artifact@v4
with:
name: workload-image-sboms
path: sbom/
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,20 @@ build/
.pytest_cache/
.mypy_cache/
*.db
node_modules/
.idea/
.vscode/

# Vendored frontend binaries — fetched at setup time by
# scripts/vendor_frontend_assets.sh, which `make setup` runs automatically.
# Kept out of git to avoid ~460 KB of binaries in the history.
#
# NOTE: the generated CSS, the NOTICE and the licence texts in demo-ui/vendor/
# are NOT ignored. They are compliance artifacts — the OFL-1.1 and MIT notices
# have to be present in the repository itself, not conjured by a build step.
demo-ui/vendor/fonts/*.woff2
demo-ui/vendor/fonts/*.woff
demo-ui/vendor/js/*.js

# sbom/ is intentionally NOT ignored — the workload image inventory is a
# compliance artifact and must be committed alongside each release.
Loading
Loading