From 6ecf22c90f10560ad2975377514f7342eaa3a102 Mon Sep 17 00:00:00 2001 From: CodesbyFebin Date: Tue, 20 Oct 2026 23:45:48 +0000 Subject: [PATCH] Follow-ups: meaningful upstream link check, model digest pins, SHA-pinned actions, licence review worksheet --- .github/workflows/ci.yml | 30 ++-- .github/workflows/links.yml | 68 +++++++++ .github/workflows/pages.yml | 8 +- .github/workflows/recipe-agent-memory.yml | 6 +- .github/workflows/refresh.yml | 10 +- .gitignore | 1 + CHANGELOG.md | 4 + CONTRIBUTING.md | 6 + CREDITS.md | 2 +- docs/credits.md | 2 +- docs/licence-review.md | 175 ++++++++++++++++++++++ docs/recipes/agent-memory-eval.md | 2 + docs/recipes/local-chat.md | 3 +- mkdocs.yml | 1 + recipes/agent-memory-eval/README.md | 2 + recipes/agent-memory-eval/models.lock | 3 + recipes/agent-memory-eval/smoke.sh | 1 + recipes/local-chat/README.md | 3 +- recipes/local-chat/models.lock | 2 + recipes/local-chat/smoke.sh | 1 + recipes/verify-models.sh | 13 ++ scripts/build.py | 5 +- scripts/upstream_links.py | 8 + 23 files changed, 318 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/links.yml create mode 100644 docs/licence-review.md create mode 100644 recipes/agent-memory-eval/models.lock create mode 100644 recipes/local-chat/models.lock create mode 100755 recipes/verify-models.sh create mode 100644 scripts/upstream_links.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2c3e2a..b8598ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,8 +9,8 @@ jobs: name: Catalog integrity runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - run: pip install -r requirements.txt - name: Schema, duplicates, licences, evidence, no submodules @@ -25,13 +25,13 @@ jobs: name: Docs build + internal links runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - run: pip install -r requirements.txt - run: mkdocs build --strict -d _check/Open-Source-Ai - name: Internal links (blocking) - uses: lycheeverse/lychee-action@v2 + uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 with: args: --offline --no-progress --index-files index.html --root-dir ${{ github.workspace }}/_check --remap 'https://codesbyfebin.github.io/Open-Source-Ai file://${{ github.workspace }}/_check/Open-Source-Ai' --exclude-path _check/Open-Source-Ai/404.html '_check/**/*.html' fail: true @@ -40,10 +40,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: {node-version: '20'} - run: pip install -r requirements.txt - run: mkdocs build --strict -d _site/Open-Source-Ai @@ -52,29 +52,19 @@ jobs: - working-directory: tests/browser run: npx playwright test - if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: playwright-results if-no-files-found: ignore path: | tests/browser/results.json tests/browser/test-results - upstream-links: - name: Upstream links (non-blocking) - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v4 - - uses: lycheeverse/lychee-action@v2 - with: - args: --no-progress --max-retries 3 --max-concurrency 8 --accept 200,206,429 --exclude localhost --exclude codesbyfebin.github.io README.md CREDITS.md 'docs/**/*.md' 'recipes/**/*.md' - fail: true recipe-local-chat: name: Recipe smoke test – local-chat runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Validate both recipe compose files run: | docker compose -f recipes/local-chat/docker-compose.yml config --quiet diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml new file mode 100644 index 0000000..9dc8d28 --- /dev/null +++ b/.github/workflows/links.yml @@ -0,0 +1,68 @@ +name: Upstream links +# Non-blocking: external sites can be flaky. Checks every upstream repo, licence and setup URL +# in the catalog plus links in README/docs/recipes. On scheduled runs, failures open/update an issue. +on: + push: {branches: [main]} + pull_request: + schedule: [{cron: '30 3 * * 1'}] # weekly, Monday 03:30 UTC + workflow_dispatch: +permissions: {contents: read} +jobs: + lychee: + name: Upstream links (non-blocking) + runs-on: ubuntu-latest + timeout-minutes: 20 + continue-on-error: true + permissions: {contents: read, issues: write} + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: {python-version: '3.12'} + - run: pip install -r requirements.txt && python scripts/upstream_links.py + - name: Check links + id: lychee + uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 + with: + token: ${{ github.token }} + fail: false + format: json + output: _links/report.json + args: >- + --no-progress --max-concurrency 8 --max-retries 4 --retry-wait-time 5 --timeout 30 + --accept '200..=206' + --exclude 'localhost|127\.0\.0\.1|codesbyfebin\.github\.io' + _links/upstream.md README.md CREDITS.md 'docs/**/*.md' 'recipes/**/*.md' + - name: Summarise + id: sum + run: | + python3 - <<'PY' + import json, os + r = json.load(open("_links/report.json")) + keys = ("total", "successful", "errors", "timeouts", "redirects", "excludes", "unknown", "cached") + stats = {k: r.get(k, 0) for k in keys} + lines = ["## Upstream link check", "", "| " + " | ".join(keys) + " |", "|" + "---|" * len(keys), "| " + " | ".join(str(stats[k]) for k in keys) + " |", ""] + for src, errs in sorted((r.get("error_map") or r.get("fail_map") or {}).items()): + for e in errs: + lines.append(f"- `{src}`: {e.get('url')} – {e.get('status', {}).get('text') if isinstance(e.get('status'), dict) else e.get('status')}") + out = "\n".join(lines) + "\n" + open("_links/summary.md", "w").write(out) + open(os.environ["GITHUB_STEP_SUMMARY"], "a").write(out) + bad = stats["errors"] + stats["timeouts"] + open(os.environ["GITHUB_OUTPUT"], "a").write(f"bad={bad}\ntotal={stats['total']}\n") + print(out) + PY + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: always() + with: {name: upstream-link-report, path: _links/} + - name: Open or update issue (scheduled runs only) + if: github.event_name == 'schedule' && steps.sum.outputs.bad != '0' + env: {GH_TOKEN: '${{ github.token }}'} + run: | + n=$(gh issue list --label link-check --state open --json number -q '.[0].number') + body="$(cat _links/summary.md) + + Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + if [ -n "$n" ]; then gh issue comment "$n" --body "$body"; else gh issue create --title "Upstream link check failures" --label link-check --body "$body" || gh issue create --title "Upstream link check failures" --body "$body"; fi + - name: Mark job failed when links are broken (still non-blocking) + if: steps.sum.outputs.bad != '0' + run: exit 1 diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 75d83b5..2ec01c8 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -8,12 +8,12 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - run: pip install -r requirements.txt - run: mkdocs build --strict - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 with: {path: site} deploy: needs: build @@ -21,4 +21,4 @@ jobs: environment: {name: github-pages, url: "${{ steps.d.outputs.page_url }}"} steps: - id: d - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.github/workflows/recipe-agent-memory.yml b/.github/workflows/recipe-agent-memory.yml index f725ce0..d3d29b3 100644 --- a/.github/workflows/recipe-agent-memory.yml +++ b/.github/workflows/recipe-agent-memory.yml @@ -15,10 +15,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 40 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: {node-version: '22'} - run: pip install -r recipes/agent-memory-eval/requirements.txt - run: ./recipes/agent-memory-eval/smoke.sh diff --git a/.github/workflows/refresh.yml b/.github/workflows/refresh.yml index 85d881c..aae6604 100644 --- a/.github/workflows/refresh.yml +++ b/.github/workflows/refresh.yml @@ -10,12 +10,12 @@ jobs: timeout-minutes: 20 permissions: {contents: write, pull-requests: write} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: {persist-credentials: false} - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: {python-version: '3.12'} - run: pip install -r requirements.txt - - uses: actions/cache@v4 + - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: .cache/etags.json key: etags-${{ github.run_id }} @@ -31,11 +31,11 @@ jobs: - name: Mint GitHub App token (optional) id: app if: ${{ vars.OSA_APP_ID != '' }} - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 with: app-id: ${{ vars.OSA_APP_ID }} private-key: ${{ secrets.OSA_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@v7 + - uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11 with: token: ${{ steps.app.outputs.token || github.token }} branch: bot/evidence-refresh diff --git a/.gitignore b/.gitignore index f576193..c47c75d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__/ .venv/ _check/ _site/ +_links/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7768a97..8233162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Catalog changes are recorded here. Evidence refreshes are listed by their pull r ## Unreleased +- Upstream link check moved to its own workflow: checks all 140 unique upstream repo/licence/setup URLs plus docs links with a GitHub token, publishes a report artifact, and opens/updates an issue on scheduled failures (still non-blocking). +- Ollama models pinned by manifest digest (`recipes/*/models.lock`), verified after pull; all GitHub Actions pinned to commit SHAs. +- Added Licence Review Worksheet (`docs/licence-review.md`) for the 15 needs-review entries. + - Licences: 15 projects now `needs-review`, each with a recorded `review_reason` (adds Phoenix, Trylon Gateway, llamafile and three AgentDomains repos: source-available or undetected). OpenMemory and PR-Agent re-verified as MIT from their LICENSE files. - Collector: failed / rate-limited lookups are reported as failed and keep previous evidence; records `moved` and `fetch_status`. Validator: moved repo and GitHub licence mismatch are errors; zone-aware date checks. - Unit tests (collector, validator, build) and Playwright browser tests (Chromium + WebKit, desktop and iPhone 13 emulation, axe-core). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3bad217..348c382 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,3 +76,9 @@ PRs created with the default `GITHUB_TOKEN` **do not trigger** other workflows ( **Option B – manual fallback (no setup):** open the PR, then either close and reopen it, or run *CI* via *Actions → CI → Run workflow* on the `bot/evidence-refresh` branch. Merge only when blocking checks are green. The refresh job never pushes to `main`, uses no `pull_request_target`, and the collection step runs before any write token is minted. + +## Pinned dependencies + +- **GitHub Actions** are pinned to full commit SHAs with the version in a comment (`uses: owner/action@ # vX.Y.Z`). To update, resolve the new tag's commit (`gh api repos///commits/ -q .sha`) and change both. +- **Ollama models** are pinned by manifest digest in `recipes//models.lock`. Get a digest with `curl -fsSL -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' https://registry.ollama.ai/v2/library//manifests/ | sha256sum`. +- **Upstream link check** (`.github/workflows/links.yml`) is non-blocking; on scheduled runs it opens or comments on an issue labelled `link-check`. diff --git a/CREDITS.md b/CREDITS.md index 0829a03..e841a86 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,7 +1,7 @@ # Credits & Licences -Open Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple or undetected licences – read the upstream terms before use. +Open Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple, source-available or undetected licences – read the upstream terms before use. Quoted clauses and open questions: [Licence Review Worksheet](docs/licence-review.md). | Project | Upstream | Licence | State | Checked | |---|---|---|---|---| diff --git a/docs/credits.md b/docs/credits.md index 0829a03..55c32fb 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -1,7 +1,7 @@ # Credits & Licences -Open Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple or undetected licences – read the upstream terms before use. +Open Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple, source-available or undetected licences – read the upstream terms before use. Quoted clauses and open questions: [Licence Review Worksheet](licence-review.md). | Project | Upstream | Licence | State | Checked | |---|---|---|---|---| diff --git a/docs/licence-review.md b/docs/licence-review.md new file mode 100644 index 0000000..a5a23b8 --- /dev/null +++ b/docs/licence-review.md @@ -0,0 +1,175 @@ +--- +title: Licence Review Worksheet – Open Source Ai +description: Worksheet for the catalog entries whose upstream licences need human review – quoted clauses and reviewer questions. +--- +# Licence Review Worksheet + +**This worksheet is not legal advice and draws no legal conclusions.** It lists the catalog entries marked *needs review*, quotes the upstream clauses that caused the flag, and lists the questions a reviewer (ideally with legal input) must answer before the catalog describes the licence differently. + +- **Source:** each upstream licence file, fetched via the GitHub API (`/repos/{owner}/{repo}/license` and, for AutoGen, `/contents/LICENSE-CODE`) on **2026-10-21**. Quotes are verbatim excerpts; read the full file before deciding. +- **The upstream file is authoritative.** Files can change; re-fetch before signing off. +- **How to record a decision:** edit the entry in `catalog/projects.yaml` (`license.label`, `license.state`, `license.review_reason`), add a CHANGELOG line naming the reviewer and date, and open a PR. Keep `needs-review` unless every question below is answered. + +General questions for every entry: + +1. Does the quoted text match the current upstream file (same commit/branch)? +2. Which parts of the repository does each licence apply to, and does the catalog description make that clear? +3. Is the catalog wording ("source-available", "multiple licences", "custom terms") accurate and neutral? + +## Summary + +| # | Project | Licence file | Why flagged | +|---|---|---|---| +| 1 | LiteLLM | [LICENSE](https://github.com/BerriAI/litellm/blob/main/LICENSE) | Separate licence for `enterprise/` | +| 2 | Open WebUI | [LICENSE](https://github.com/open-webui/open-webui/blob/main/LICENSE) | Branding clause added to BSD-style terms | +| 3 | Lobe Chat | [LICENSE](https://github.com/lobehub/lobehub/blob/canary/LICENSE) | Apache-2.0 plus commercial-licence condition | +| 4 | AutoGen | [LICENSE](https://github.com/microsoft/autogen/blob/main/LICENSE), [LICENSE-CODE](https://github.com/microsoft/autogen/blob/main/LICENSE-CODE) | Two licence files (CC-BY-4.0 and MIT) | +| 5 | Dify | [LICENSE](https://github.com/langgenius/dify/blob/main/LICENSE) | Modified Apache-2.0 with extra conditions | +| 6 | Flowise | [LICENSE.md](https://github.com/FlowiseAI/Flowise/blob/main/LICENSE.md) | Commercial licence for enterprise code | +| 7 | n8n | [LICENSE.md](https://github.com/n8n-io/n8n/blob/master/LICENSE.md) | Sustainable Use License + enterprise files | +| 8 | Weaviate | [LICENSE](https://github.com/weaviate/weaviate/blob/main/LICENSE) | BSD-3-Clause outside `wl/`, enterprise inside | +| 9 | Langfuse | [LICENSE](https://github.com/langfuse/langfuse/blob/main/LICENSE) | Separate licence for `ee/` directories | +| 10 | Phoenix | [LICENSE](https://github.com/Arize-ai/phoenix/blob/main/LICENSE) | Elastic License 2.0 | +| 11 | Trylon Gateway | [LICENSE](https://github.com/trylonai/gateway/blob/main/LICENSE) | Business Source License 1.1 | +| 12 | llamafile | [LICENSE](https://github.com/mozilla-ai/llamafile/blob/main/LICENSE) | GitHub detection NOASSERTION; bundled third-party code | +| 13–15 | AgentDomains, AgentDomains MCP, AgentDomains Skill | [LICENSE](https://github.com/tashfeenahmed/AgentDomains/blob/main/LICENSE) ([MCP](https://github.com/tashfeenahmed/AgentDomains-mcp/blob/main/LICENSE), [Skill](https://github.com/tashfeenahmed/AgentDomains-skill/blob/main/LICENSE)) | Functional Source License 1.1 | + +## 1. LiteLLM + +> All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE". +> +> Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below. + +Questions: What are the terms of `enterprise/LICENSE`? Does the recipe's container image (`ghcr.io/berriai/litellm`) include enterprise code, and under what conditions is it usable? Should the catalog label read "MIT (core) + separate enterprise licence"? + +## 2. Open WebUI + +> 4. Notwithstanding any other provision of this License, and as a material condition of the rights granted herein, licensees are strictly prohibited from altering, removing, obscuring, or replacing any "Open WebUI" branding […] in any deployment or distribution, except in the following circumstances: (i) deployments or distributions where the total number of end users […] does not exceed fifty (50) within any rolling thirty (30) day period; (ii) the licensee has obtained specific prior written permission from the copyright holder; or (iii) where the licensee has obtained a duly executed enterprise license expressly permitting such modification. + +> Materials governed by prior licenses retain those original license terms, as specified in LICENSE_HISTORY. + +Questions: How should the catalog describe a BSD-3-style licence with an added branding condition? Which versions are covered by `LICENSE_HISTORY`, and does that affect the pinned recipe version (v0.12.3)? Does the recipe (unmodified image) raise any branding concern? (It does not alter branding.) + +## 3. Lobe Chat + +> From 1.0, LobeChat is licensed under the LobeHub Community License, based on Apache License 2.0 with the following additional conditions: +> +> b. a commercial license must be obtained from the producer if you want to develop and distribute a derivative work based on LobeChat. + +> a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary. + +Questions: What counts as a "derivative work" here? How should the catalog convey that distributing modified versions needs a commercial licence? Does the "adjust the agreement" clause affect how often this entry must be re-reviewed? + +## 4. AutoGen + +`LICENSE` begins: + +> Attribution 4.0 International + +`LICENSE-CODE` begins: + +> MIT License +> +> Copyright (c) Microsoft Corporation. + +Questions: Does the repository state which files fall under CC-BY-4.0 (documentation) and which under MIT (code)? GitHub reports only CC-BY-4.0 — should the catalog label list both, as it does now? + +## 5. Dify + +> Dify is licensed under a modified version of the Apache License 2.0, with the following additional conditions: + +> a. Multi-tenant service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment. + +> b. LOGO and copyright information: In the process of using Dify's frontend, you may not remove or modify the LOGO or copyright information in the Dify console or applications. + +Questions: How should "one tenant corresponds to one workspace" be summarised for readers? Is "modified Apache-2.0 with additional conditions" a fair label? Should the catalog mention the multi-tenant condition on the project page? + +## 6. Flowise + +> All content that resides under https://github.com/FlowiseAI/Flowise/tree/main/packages/server/src/enterprise directory and files with explicit copyright notice such as [IdentityManager.ts](…) are licensed under [Commercial License](…). + +> Content outside of the above mentioned directories or restrictions above is available under the "Apache 2.0" license as defined below. + +Questions: Which files outside `enterprise/` carry "explicit copyright notice" and therefore the commercial licence? The repository is also **archived upstream** (see freshness report) — should the entry be kept, and with what note? + +## 7. n8n + +> Content of branches other than the main branch (i.e. "master") are not licensed. + +> Source code files that contain ".ee." in their filename or ".ee" in their dirname are NOT licensed under the Sustainable Use License. + +> You may use or modify the software only for your own internal business purposes or for non-commercial or personal use. You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes. + +Questions: Is "source-available, not OSI" the right category label? How should the catalog phrase the internal-business-use limitation neutrally? Should the selection criteria explicitly allow such licences? + +## 8. Weaviate + +> Outside of the "wl" directory, source code in a given file is licensed under the BSD-3-Clause License, as defined in LICENSE-BSD. + +> Within the "wl" directory and all its sub-directories, source code is Copyright Weaviate BV 2026-present, all rights reserved, and is only available under a separate enterprise license. + +Questions: Does the standard distribution (binaries/images) include `wl/` code, and is it gated by licence key as the file says ("Enterprise features are enabled via license key")? Is "BSD-3-Clause (core)" an adequate label? + +## 9. Langfuse + +> All content that resides under the "ee/", "web/src/ee/", and/or "worker/src/ee/" directories of this repository, if these directories exist, is licensed under the license defined in "ee/LICENSE". + +> Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below. + +Questions: What are the terms of `ee/LICENSE`? Are `ee/` features included in self-hosted images by default? + +## 10. Phoenix + +> Elastic License 2.0 (ELv2) + +> You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software. + +> You may not move, change, disable, or circumvent the license key functionality in the software […] + +Questions: Is "source-available, not OSI" the right label? Should the selection criteria say how Elastic-licensed projects are treated? + +## 11. Trylon Gateway + +> BUSINESS SOURCE LICENSE 1.1 + +> Use Limitation: You may not use the Licensed Work for production purposes if you are providing a software-as-a-service (SaaS) offering that utilizes the Licensed Work to supply large language model (LLM) security guardrail capabilities to third parties, and this offering competes directly with Trylon AI's commercial cloud services. + +> Change Date: 2028-04-03 +> +> Change License: Apache License, Version 2.0 + +> The Licensed Work is the code specified in the NOTICE file. + +Questions: What does the NOTICE file include as the "Licensed Work"? Should the catalog show the change date and change licence? When should this entry be re-reviewed (on or after 2028-04-03)? + +## 12. llamafile + +The licence file is short Apache-2.0 boilerplate: + +> The Apache 2.0 License +> +> Copyright 2023 Mozilla Foundation +> +> Licensed under the Apache License, Version 2.0 (the "License"); + +GitHub's detector returns `NOASSERTION` for it. The repository root (listed via the API on 2026-10-21) also contains `.gitmodules`, `third_party/`, `llama.cpp`, `whisper.cpp` and `stable-diffusion.cpp` entries. + +Questions: Do the bundled/vendored components carry other licences that apply to distributed llamafile binaries? If the Apache-2.0 file covers the project's own code, can the entry be `declared: Apache-2.0` with a note about third-party components? + +## 13–15. AgentDomains, AgentDomains MCP, AgentDomains Skill + +All three repositories have byte-identical licence files (checked 2026-10-21): + +> # Functional Source License, Version 1.1, Apache 2.0 Future License + +> A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that: +> +> 1. substitutes for the Software; +> +> 2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or +> +> 3. offers the same or substantially similar functionality as the Software. + +> We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. + +Questions: `FSL-1.1-Apache-2.0` is an SPDX identifier — should the catalog use it as the label with state `needs-review` (source-available), as now? Is the two-year conversion per release, and should the catalog note it? diff --git a/docs/recipes/agent-memory-eval.md b/docs/recipes/agent-memory-eval.md index b95b48e..4a00bd1 100644 --- a/docs/recipes/agent-memory-eval.md +++ b/docs/recipes/agent-memory-eval.md @@ -21,6 +21,8 @@ description: A small Python agent uses a local model via Ollama, stores long-ter | promptfoo | `promptfoo@0.124.1` | | Models | `qwen2.5:1.5b` (~1 GB), `nomic-embed-text:v1.5` (~0.3 GB) | +Model digests are pinned in `models.lock`; `smoke.sh` runs `../verify-models.sh` after the pull and fails if the tag now points to different weights. To update, pull the new tag, record its digest and say so in CHANGELOG. + ## Requirements - Docker 24+ with Compose v2, Python 3.10+, Node.js 22.22+ (promptfoo 0.124 requirement) diff --git a/docs/recipes/local-chat.md b/docs/recipes/local-chat.md index 0c2cd5d..ef717e5 100644 --- a/docs/recipes/local-chat.md +++ b/docs/recipes/local-chat.md @@ -11,6 +11,8 @@ description: Ollama serves a small local model, LiteLLM exposes it as an OpenAI- **Data flow:** browser → Open WebUI (:3000) → LiteLLM gateway (:4000, OpenAI-compatible) → Ollama (:11434) → local model. Nothing leaves your machine after the first image/model download. +Model digests are pinned in `models.lock`; `smoke.sh` runs `../verify-models.sh` after the pull and fails if the tag now points to different weights. To update, pull the new tag, record its digest and say so in CHANGELOG. + ## Requirements | | Minimum (tested) | For bigger models | @@ -68,4 +70,3 @@ docker compose down -v # also delete models and chats ## Known limitations - `qwen2.5:0.5b` is a tiny model chosen so CI can run on CPU; answers are low quality. Use a 3B–8B model for real use. -- Ollama model tags are not immutable; the digest actually pulled is printed by `ollama list` in the CI log. diff --git a/mkdocs.yml b/mkdocs.yml index 8629292..9000b47 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -54,6 +54,7 @@ nav: - Freshness Report: freshness.md - Selection Criteria: selection-criteria.md - Credits & Licences: credits.md + - Licence Review Worksheet: licence-review.md exclude_docs: | wiki/ diff --git a/recipes/agent-memory-eval/README.md b/recipes/agent-memory-eval/README.md index f4ecf9f..c487d4e 100644 --- a/recipes/agent-memory-eval/README.md +++ b/recipes/agent-memory-eval/README.md @@ -12,6 +12,8 @@ | promptfoo | `promptfoo@0.124.1` | | Models | `qwen2.5:1.5b` (~1 GB), `nomic-embed-text:v1.5` (~0.3 GB) | +Model digests are pinned in `models.lock`; `smoke.sh` runs `../verify-models.sh` after the pull and fails if the tag now points to different weights. To update, pull the new tag, record its digest and say so in CHANGELOG. + ## Requirements - Docker 24+ with Compose v2, Python 3.10+, Node.js 22.22+ (promptfoo 0.124 requirement) diff --git a/recipes/agent-memory-eval/models.lock b/recipes/agent-memory-eval/models.lock new file mode 100644 index 0000000..00fc80b --- /dev/null +++ b/recipes/agent-memory-eval/models.lock @@ -0,0 +1,3 @@ +# Ollama manifest digests (sha256 of registry manifest), recorded 2026-10-21 from registry.ollama.ai +qwen2.5:1.5b sha256:65ec06548149b04c096a120e4a6da9d4017ea809c91734ea5631e89f96ddc57b +nomic-embed-text:v1.5 sha256:0a109f422b47e3a30ba2b10eca18548e944e8a23073ee3f3e947efcf3c45e59f diff --git a/recipes/agent-memory-eval/smoke.sh b/recipes/agent-memory-eval/smoke.sh index dac8289..3ddb1e6 100755 --- a/recipes/agent-memory-eval/smoke.sh +++ b/recipes/agent-memory-eval/smoke.sh @@ -9,6 +9,7 @@ start=$(date +%s) docker compose config --quiet docker compose up -d docker compose wait model-pull +../verify-models.sh . for i in $(seq 1 30); do curl -fsS http://localhost:6333/readyz >/dev/null 2>&1 && break; sleep 2; done python agent.py remember "My favourite programming language is Rust." python agent.py remember "I live in Lisbon, Portugal." diff --git a/recipes/local-chat/README.md b/recipes/local-chat/README.md index 3bfa1d4..eea644a 100644 --- a/recipes/local-chat/README.md +++ b/recipes/local-chat/README.md @@ -2,6 +2,8 @@ **Data flow:** browser → Open WebUI (:3000) → LiteLLM gateway (:4000, OpenAI-compatible) → Ollama (:11434) → local model. Nothing leaves your machine after the first image/model download. +Model digests are pinned in `models.lock`; `smoke.sh` runs `../verify-models.sh` after the pull and fails if the tag now points to different weights. To update, pull the new tag, record its digest and say so in CHANGELOG. + ## Requirements | | Minimum (tested) | For bigger models | @@ -59,4 +61,3 @@ docker compose down -v # also delete models and chats ## Known limitations - `qwen2.5:0.5b` is a tiny model chosen so CI can run on CPU; answers are low quality. Use a 3B–8B model for real use. -- Ollama model tags are not immutable; the digest actually pulled is printed by `ollama list` in the CI log. diff --git a/recipes/local-chat/models.lock b/recipes/local-chat/models.lock new file mode 100644 index 0000000..0fb581e --- /dev/null +++ b/recipes/local-chat/models.lock @@ -0,0 +1,2 @@ +# Ollama manifest digests (sha256 of registry manifest), recorded 2026-10-21 from registry.ollama.ai +qwen2.5:0.5b sha256:a8b0c51577010a279d933d14c2a8ab4b268079d44c5c8830c0a93900f1827c67 diff --git a/recipes/local-chat/smoke.sh b/recipes/local-chat/smoke.sh index 559c30f..514d1f4 100755 --- a/recipes/local-chat/smoke.sh +++ b/recipes/local-chat/smoke.sh @@ -11,6 +11,7 @@ start=$(date +%s) docker compose config --quiet docker compose up -d docker compose wait model-pull +../verify-models.sh . for i in $(seq 1 60); do curl -fsS http://localhost:4000/health/liveliness >/dev/null 2>&1 && break; sleep 5; done resp=$(curl -fsS http://localhost:4000/v1/chat/completions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' \ -d '{"model":"local-llama","temperature":0,"messages":[{"role":"user","content":"What is 17 plus 25? Answer with the number only."}],"max_tokens":20}') diff --git a/recipes/verify-models.sh b/recipes/verify-models.sh new file mode 100755 index 0000000..1337bb8 --- /dev/null +++ b/recipes/verify-models.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Verify pulled Ollama models match the manifest digests pinned in models.lock. +# Ollama pulls by tag; tags can be re-pointed upstream, so we fail on mismatch. +# Usage: verify-models.sh (reads /models.lock: ": sha256:") +set -euo pipefail +dir="$1"; fail=0 +while read -r model digest; do + [ -z "$model" ] || [ "${model:0:1}" = "#" ] && continue + name="${model%%:*}"; tag="${model#*:}" + actual="sha256:$(docker compose --project-directory "$dir" exec -T ollama sh -c "sha256sum /root/.ollama/models/manifests/registry.ollama.ai/library/$name/$tag" | cut -c1-64)" + if [ "$actual" = "$digest" ]; then echo "OK $model $actual"; else echo "FAIL $model expected $digest got $actual"; fail=1; fi +done < "$dir/models.lock" +exit $fail diff --git a/scripts/build.py b/scripts/build.py index 6822cd4..a182ef0 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -186,8 +186,8 @@ def w(path, text): The MIT [LICENSE](LICENSE) © CodesbyFebin covers only this repository's own curation, docs and code. Upstream projects are governed by their own licences – see [CREDITS.md](CREDITS.md). """) -cr = GEN + "# Credits & Licences\n\nOpen Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple or undetected licences – read the upstream terms before use.\n\n| Project | Upstream | Licence | State | Checked |\n|---|---|---|---|---|\n" + "\n".join(f"| {p['name']} | {p['repo']} | {p['license']['label']} | {p['license']['state']} | {p['license']['checked']} |" for p in projects) + "\n" -w("CREDITS.md", cr); w("docs/credits.md", cr.replace("](", "](")) +cr = GEN + "# Credits & Licences\n\nOpen Source Ai's own content is MIT-licensed © CodesbyFebin. Catalogued projects are not included in this repository; each is governed by its upstream licence, which is authoritative. Entries marked **needs review** have custom, multiple, source-available or undetected licences – read the upstream terms before use. Quoted clauses and open questions: [Licence Review Worksheet](licence-review.md).\n\n| Project | Upstream | Licence | State | Checked |\n|---|---|---|---|---|\n" + "\n".join(f"| {p['name']} | {p['repo']} | {p['license']['label']} | {p['license']['state']} | {p['license']['checked']} |" for p in projects) + "\n" +w("CREDITS.md", cr.replace("](licence-review.md)", "](docs/licence-review.md)")); w("docs/credits.md", cr.replace("](", "](")) # ---- nav m = open("mkdocs.yml").read().split("nav:")[0] @@ -208,6 +208,7 @@ def w(path, text): - Freshness Report: freshness.md - Selection Criteria: selection-criteria.md - Credits & Licences: credits.md + - Licence Review Worksheet: licence-review.md """ + tail) # ---- recipe pages diff --git a/scripts/upstream_links.py b/scripts/upstream_links.py new file mode 100644 index 0000000..bae928d --- /dev/null +++ b/scripts/upstream_links.py @@ -0,0 +1,8 @@ +"""Write every upstream URL from catalog/projects.yaml (repo, licence evidence, setup) to _links/upstream.md for lychee.""" +import os, yaml +P = yaml.safe_load(open("catalog/projects.yaml"))["projects"] +urls = sorted({u.split("#")[0] for p in P for u in (p["repo"], p["license"]["evidence_url"], p["setup_url"])}) +os.makedirs("_links", exist_ok=True) +with open("_links/upstream.md", "w") as f: + f.write("# Upstream URLs\n\n" + "\n".join(f"- <{u}>" for u in urls) + "\n") +print(f"{len(urls)} unique upstream URLs from {len(P)} projects")