Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ MODELPORT_ADMIN_PASSWORD=replace-with-a-long-random-admin-password
# Optional for a confidential client; omit for a supported public client.
# MODELPORT_OIDC_CLIENT_SECRET=replace-with-client-secret
# MODELPORT_OIDC_LABEL=Company SSO
# After verifying a linked SSO administrator, optionally enforce SSO/MFA class:
# MODELPORT_PASSWORD_LOGIN_ENABLED=0
# MODELPORT_OIDC_REQUIRED_ACR=urn:example:authentication:mfa
# Automatic user provisioning is off by default; pre-create users initially.
# MODELPORT_OIDC_AUTO_PROVISION=0
# MODELPORT_OIDC_USERNAME_CLAIM=preferred_username
Expand Down
11 changes: 11 additions & 0 deletions .github/MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ Protect `main` with:
- restricted force pushes and branch deletion;
- signed commits and tags where the signing setup is available.

The Beta repository enforces PRs, the five GitHub Actions checks (including both
CodeQL languages), current branches and resolved conversations for administrators
too. Force pushes and branch deletion are disabled. The independent-approval
count is currently zero: a second release/security maintainer has not completed
the continuity gate below. Do not describe this as enforced two-person review.
Raise the required review count and enable CODEOWNERS review after that handoff.
The active version-tag ruleset prohibits updating or deleting `v*` tags without
a bypass. Private vulnerability reporting, automatic Dependabot security
updates and immutable publication are enabled. Existing older Releases are not
retroactively made immutable by that setting.

Routine CI uses the pinned Rust and Node versions, locked dependencies,
fmt/test/clippy, dashboard checks, shell syntax, examples, and documentation
links through `scripts/check-all.sh`. Paid Provider tests require an explicit,
Expand Down
24 changes: 16 additions & 8 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ Older snapshots may not receive backports unless a release notice says so.

## Dependency Audit Exceptions

Rust and dashboard dependency audits fail CI. A temporary exception is allowed
only in `dashboard/npm-audit-exceptions.json`, with an exact advisory/package
pair, an expiry date, and a deployment-specific exposure analysis. The audit
fails for every unlisted advisory, expired exception, or stale exception.
Rust and dashboard dependency audits fail CI. Dashboard exceptions live in
`dashboard/npm-audit-exceptions.json`, with an exact advisory/package pair,
an expiry date, and a deployment-specific exposure analysis. Rust exceptions
are recorded in `.cargo/audit.toml` and `deny.toml`; the current RSA exception
covers OIDC public-key verification, with the dependency path and removal
condition documented there. Unlisted advisories fail CI; dashboard exceptions
also fail when expired or stale.
Exceptions are risk acceptance records, not claims that an affected package is
generally safe.

Expand Down Expand Up @@ -63,6 +66,11 @@ Do not place exploit details, provider keys, session tokens, backups, or a full
process-local and reset on restart.
- Session cookies are HttpOnly and SameSite=Lax. Set
`MODELPORT_ADMIN_COOKIE_SECURE=1` whenever the dashboard is served over HTTPS.
- `MODELPORT_PASSWORD_LOGIN_ENABLED=0` enforces OIDC-only login at the backend.
Startup requires an active administrator already linked to that issuer.
`MODELPORT_OIDC_REQUIRED_ACR` additionally requires an exact signed `acr`
claim and forbids password fallback. Configure the identity provider's MFA
policy for that class; ModelPort does not implement an MFA factor itself.
- Dashboard writes require a session, `X-ModelPort-CSRF`, and an allowed
Origin/Referer when present. `MODELPORT_ALLOWED_ORIGINS` extends that write
check; it does not enable browser CORS.
Expand Down Expand Up @@ -91,10 +99,10 @@ Do not place exploit details, provider keys, session tokens, backups, or a full
internal upstream; local/custom runtimes retain HTTP support for controlled
local integration. The HTTP override does not disable private/metadata-IP
protection.
- Hostnames are not currently pinned or revalidated after DNS resolution. A
hostname that resolves to an internal address is outside the current SSRF
guard. Use outbound firewall rules or an allowlist when administrators are not
fully trusted.
- Provider hostnames are resolved and every resulting address is checked before
egress. Validated addresses are pinned to the HTTP connection, environment
proxies are disabled, and private/metadata addresses require explicit policy.
Keep outbound firewall rules or an allowlist as an independent control.
- Upstream HTTP redirects are disabled and every GET/POST/SSE handshake requires
2xx. A 3xx is treated as an upstream failure and mapped to client-facing 502,
not followed or exposed as a client redirect. Upstream non-stream bodies and
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ jobs:

permissions:
contents: read
attestations: read

services:
postgres:
Expand Down Expand Up @@ -268,6 +269,36 @@ jobs:
sed -n '1,200p' "${RUNNER_TEMP}/modelport-e2e.log"
exit 1

- name: Verify previous release rollback artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
mkdir -p "$RUNNER_TEMP/modelport-rollback"
gh release download v0.1.1 --repo tiammomo/ModelPort \
--pattern model-port-v0.1.1-linux-amd64.tar.gz --pattern SHA256SUMS \
--dir "$RUNNER_TEMP/modelport-rollback"
cd "$RUNNER_TEMP/modelport-rollback"
sha256sum --check --ignore-missing SHA256SUMS
gh attestation verify model-port-v0.1.1-linux-amd64.tar.gz --repo tiammomo/ModelPort
tar -xzf model-port-v0.1.1-linux-amd64.tar.gz \
model-port-v0.1.1-linux-amd64/model-port

- name: Run isolated authentication, protocol and recovery acceptance
env:
MODELPORT_TEST_BINARY: ${{ github.workspace }}/target/debug/model-port
MODELPORT_ROLLBACK_TEST_BINARY: ${{ runner.temp }}/modelport-rollback/model-port-v0.1.1-linux-amd64/model-port
MODELPORT_ASSURANCE_OUTPUT_DIR: ${{ runner.temp }}/modelport-assurance
run: ./scripts/acceptance.sh --isolated

- name: Upload isolated acceptance evidence
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: isolated-production-acceptance
path: ${{ runner.temp }}/modelport-assurance
if-no-files-found: ignore
retention-days: 14

- name: Run dashboard E2E
run: npm --prefix dashboard run e2e

Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ jobs:
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
with:
persist-credentials: false
fetch-depth: 0

- name: Require a commit on protected main
env:
GH_TOKEN: ${{ github.token }}
run: |
git merge-base --is-ancestor HEAD origin/main
test "$(gh api "repos/$GITHUB_REPOSITORY/branches/main" --jq '.protected')" = true

- name: Match tag to backend and dashboard versions
run: |
Expand All @@ -50,6 +58,7 @@ jobs:
uses: ./.github/workflows/ci.yml
permissions:
contents: read
attestations: read

binary:
name: Build and attest binary
Expand Down Expand Up @@ -305,4 +314,10 @@ jobs:
--verify-tag \
--generate-notes \
--title "$release_title" \
--draft \
"${release_flags[@]}"
# All assets must be present before immutable publication locks them.
expected="$(find dist -maxdepth 1 -type f | wc -l)"
actual="$(gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets | length')"
test "$actual" -eq "$expected"
gh release edit "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --draft=false
30 changes: 29 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ All notable ModelPort changes are recorded here. The project follows

## [Unreleased]

## [0.1.2] - 2026-09-07

### Authentication and production acceptance

- Add optional backend-enforced SSO-only login and exact signed OIDC `acr`
requirements. Reject password fallback when assurance is required and refuse
SSO-only startup without an active administrator linked to the configured
issuer. Existing deployments retain password login unless explicitly changed.
- Add isolated signed OIDC/PKCE, Messages/Chat Completions Tool Use, distinct-user
concurrent streaming, cancellation, database interruption, restore and actual
v0.1.1 binary rollback acceptance to the PostgreSQL CI gate. Record synthetic
evidence separately from real Provider, GPU, IdP and production RTO/RPO claims.
- Require release commits on protected main and upload draft assets before
immutable publication. Repository branch/tag protection, vulnerability
reporting and automated security-update settings are enabled separately.
- Correct stale DNS-pinning, migration and production-readiness documentation.

### Internal maintenance

- Group repository policies, fixtures and deployment assets by ownership.
- Separate ledger reporting and provider credential management, share
credential readiness and correct credential-pool readiness in route summaries.
- Consolidate native lifecycle and checks in `scripts/dev.sh`, preserve legacy
entries, resolve commands from their checkout and stop only owned processes.
- Include embedded resources and workspace inputs in native build freshness
checks; centralize repeated development and troubleshooting procedures.

## [0.1.1] - 2026-09-06

### Release correction
Expand Down Expand Up @@ -116,6 +143,7 @@ Back up PostgreSQL and run a restore drill before upgrading. Compose still uses
the PostgreSQL 18 volume `modelport_modelport-postgres-18`; export any older
volume before removing it.

[Unreleased]: https://github.com/tiammomo/ModelPort/compare/v0.1.1...HEAD
[Unreleased]: https://github.com/tiammomo/ModelPort/compare/v0.1.2...HEAD
[0.1.2]: https://github.com/tiammomo/ModelPort/releases/tag/v0.1.2
[0.1.1]: https://github.com/tiammomo/ModelPort/releases/tag/v0.1.1
[0.1.0]: https://github.com/tiammomo/ModelPort/tree/v0.1.0
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "model-port"
version = "0.1.1"
version = "0.1.2"
edition = "2024"
rust-version = "1.96"
description = "A self-hosted multi-protocol model gateway for Anthropic and OpenAI-compatible workflows."
Expand Down Expand Up @@ -29,7 +29,7 @@ axum = { version = "0.8", features = ["macros"] }
futures-util = "0.3"
httpdate = "1"
jsonschema = { version = "0.48", default-features = false }
modelport-ops-protocol = { version = "0.1.1", path = "crates/ops-protocol" }
modelport-ops-protocol = { version = "0.1.2", path = "crates/ops-protocol" }
openidconnect = { version = "4.0.1", default-features = false, features = ["reqwest", "rustls-tls"] }
rand_core = { version = "0.6", features = ["getrandom"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1

ARG RUST_VERSION=1.96.0
ARG MODELPORT_VERSION=0.1.1
ARG MODELPORT_VERSION=0.1.2
ARG MODELPORT_SOURCE_REVISION=unknown
ARG MODELPORT_SOURCE_STATE=unknown
ARG MODELPORT_BUILD_DATE=unknown
Expand Down
4 changes: 2 additions & 2 deletions crates/ops-agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "modelport-ops-agent"
version = "0.1.1"
version = "0.1.2"
edition = "2024"
rust-version = "1.96"
description = "Deterministic, read-only operations agent for ModelPort."
Expand All @@ -10,7 +10,7 @@ publish = false
[dependencies]
anyhow = "1"
axum = "0.8"
modelport-ops-protocol = { version = "0.1.1", path = "../ops-protocol" }
modelport-ops-protocol = { version = "0.1.2", path = "../ops-protocol" }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde_json = "1"
sha2 = "0.10"
Expand Down
2 changes: 1 addition & 1 deletion crates/ops-agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1

ARG RUST_VERSION=1.96.0
ARG MODELPORT_VERSION=0.1.1
ARG MODELPORT_VERSION=0.1.2
ARG MODELPORT_SOURCE_REVISION=unknown
ARG MODELPORT_SOURCE_STATE=unknown
ARG MODELPORT_BUILD_DATE=unknown
Expand Down
2 changes: 1 addition & 1 deletion crates/ops-protocol/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "modelport-ops-protocol"
version = "0.1.1"
version = "0.1.2"
edition = "2024"
rust-version = "1.96"
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion dashboard/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1

ARG MODELPORT_VERSION=0.1.1
ARG MODELPORT_VERSION=0.1.2
ARG MODELPORT_SOURCE_REVISION=unknown
ARG MODELPORT_SOURCE_STATE=unknown
ARG MODELPORT_BUILD_DATE=unknown
Expand Down
4 changes: 2 additions & 2 deletions dashboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dashboard/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dashboard",
"private": true,
"version": "0.1.1",
"version": "0.1.2",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
3 changes: 3 additions & 0 deletions deploy/docker/modelport.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ MODELPORT_ADMIN_SESSION_TTL_SECONDS=43200
# Optional confidential-client secret.
# MODELPORT_OIDC_CLIENT_SECRET=replace-with-client-secret
# MODELPORT_OIDC_LABEL=Company SSO
# After verifying a linked SSO administrator, optionally enforce SSO/MFA class:
# MODELPORT_PASSWORD_LOGIN_ENABLED=0
# MODELPORT_OIDC_REQUIRED_ACR=urn:example:authentication:mfa
# Automatic provisioning is off by default; pre-create users initially.
# MODELPORT_OIDC_AUTO_PROVISION=0
# MODELPORT_OIDC_USERNAME_CLAIM=preferred_username
Expand Down
6 changes: 3 additions & 3 deletions deploy/release/compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ services:
max-file: "${MODELPORT_LOG_MAX_FILES:-5}"

modelport:
image: ${MODELPORT_IMAGE:-ghcr.io/tiammomo/modelport:0.1.1}
image: ${MODELPORT_IMAGE:-ghcr.io/tiammomo/modelport:0.1.2}
pull_policy: ${MODELPORT_PULL_POLICY:-missing}
init: true
read_only: true
Expand Down Expand Up @@ -86,7 +86,7 @@ services:
max-file: "${MODELPORT_LOG_MAX_FILES:-5}"

dashboard:
image: ${MODELPORT_DASHBOARD_IMAGE:-ghcr.io/tiammomo/modelport-dashboard:0.1.1}
image: ${MODELPORT_DASHBOARD_IMAGE:-ghcr.io/tiammomo/modelport-dashboard:0.1.2}
pull_policy: ${MODELPORT_PULL_POLICY:-missing}
init: true
read_only: true
Expand All @@ -108,7 +108,7 @@ services:

ops-agent:
profiles: ["ops-agent"]
image: ${MODELPORT_OPS_AGENT_IMAGE:-ghcr.io/tiammomo/modelport-ops-agent:0.1.1}
image: ${MODELPORT_OPS_AGENT_IMAGE:-ghcr.io/tiammomo/modelport-ops-agent:0.1.2}
pull_policy: ${MODELPORT_PULL_POLICY:-missing}
init: true
read_only: true
Expand Down
3 changes: 3 additions & 0 deletions deploy/systemd/modelport.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ MODELPORT_ADMIN_SESSION_TTL_SECONDS=43200
# Optional confidential-client secret.
# MODELPORT_OIDC_CLIENT_SECRET=replace-with-client-secret
# MODELPORT_OIDC_LABEL=Company SSO
# After verifying a linked SSO administrator, optionally enforce SSO/MFA class:
# MODELPORT_PASSWORD_LOGIN_ENABLED=0
# MODELPORT_OIDC_REQUIRED_ACR=urn:example:authentication:mfa
# Automatic provisioning is off by default; pre-create users initially.
# MODELPORT_OIDC_AUTO_PROVISION=0
# MODELPORT_OIDC_USERNAME_CLAIM=preferred_username
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ services:
context: .
dockerfile: Dockerfile
args:
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.1}
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.2}
MODELPORT_SOURCE_REVISION: ${MODELPORT_SOURCE_REVISION:-unknown}
MODELPORT_SOURCE_STATE: ${MODELPORT_SOURCE_STATE:-unknown}
MODELPORT_BUILD_DATE: ${MODELPORT_BUILD_DATE:-unknown}
Expand Down Expand Up @@ -99,7 +99,7 @@ services:
context: .
dockerfile: dashboard/Dockerfile
args:
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.1}
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.2}
MODELPORT_SOURCE_REVISION: ${MODELPORT_SOURCE_REVISION:-unknown}
MODELPORT_SOURCE_STATE: ${MODELPORT_SOURCE_STATE:-unknown}
MODELPORT_BUILD_DATE: ${MODELPORT_BUILD_DATE:-unknown}
Expand Down Expand Up @@ -129,7 +129,7 @@ services:
context: .
dockerfile: crates/ops-agent/Dockerfile
args:
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.1}
MODELPORT_VERSION: ${MODELPORT_VERSION:-0.1.2}
MODELPORT_SOURCE_REVISION: ${MODELPORT_SOURCE_REVISION:-unknown}
MODELPORT_SOURCE_STATE: ${MODELPORT_SOURCE_STATE:-unknown}
MODELPORT_BUILD_DATE: ${MODELPORT_BUILD_DATE:-unknown}
Expand Down
Loading
Loading