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
50 changes: 50 additions & 0 deletions .github/workflows/platform-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Platform manifest and source boundary

# Validates platform-manifest.json against the real published artifacts
# (PyPI, status.json) and enforces the open-core source boundary.
# See docs/platform-manifest.md and scripts/check_source_boundary.py.

on:
pull_request:
branches:
- '**'
push:
branches:
- main
schedule:
# Weekly drift check: a component release without a manifest regeneration
# should fail here, loudly, even if no PR touches this repo.
- cron: '17 6 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
validate-platform-manifest:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.12'

- name: Validate platform manifest against published artifacts
run: python scripts/validate_platform_manifest.py

check-source-boundary:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.12'

- name: Check open-core source boundary
run: python scripts/check_source_boundary.py
34 changes: 34 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,40 @@ replay, repair, policy, and backend work belongs there.
- Update documentation as needed
- Keep PRs focused and small

## Licensing and the open-core boundary

OpenAdapt is open-core. The public repositories (this launcher,
`openadapt-flow`, `openadapt-capture`, `openadapt-desktop`, and the other
`openadapt-*` engine repos) are MIT licensed: the mechanism and the public
interfaces are open. The hosted control plane (OpenAdapt Cloud) is a
proprietary commercial service, and certain data and empirical tuning stay
private by policy.

What this means for contributions to the public repos:

- Do not contribute application-specific recipes, customer fixtures, or
proprietary system identifiers derived from real deployments (for example,
automation content tied to a specific customer's EHR configuration). CI
runs `scripts/check_source_boundary.py` to reject that class of content.
- Do not contribute deployment-derived corpora, tuned adversary parameters,
thresholds, oracle or connector recipes, or datasets tied to real systems
of record. Synthetic, reproducible fixtures are welcome.
- Do not copy or vendor GPL/AGPL/SSPL or otherwise non-MIT-compatible
material into these repositories or their built packages.
- The OpenAdapt name and logo are trademarks and are not covered by the MIT
License; see [TRADEMARKS.md](TRADEMARKS.md).

## Developer Certificate of Origin

By submitting a contribution you certify the
[Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org/):
that you wrote the contribution or otherwise have the right to submit it
under the MIT License. Signing off your commits (`git commit -s`) is
appreciated but the certification applies to every contribution regardless.

A Contributor License Agreement (CLA) is under consideration but has not
been adopted; today the DCO plus the MIT License govern contributions.

## Questions?

- [Discord](https://discord.gg/yF527cQbDG)
Expand Down
41 changes: 41 additions & 0 deletions TRADEMARKS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# OpenAdapt Trademark Policy

The OpenAdapt name, the OpenAdapt logo, and the openadapt.ai domain are
trademarks of MLDSAI Inc. (operating as OpenAdapt.AI).

The MIT License that covers the source code in this repository and the other
public OpenAdapt repositories covers the code only. It does not grant any
rights to use the OpenAdapt name or logo.

## What you may do without asking

- Nominative use: accurately referring to OpenAdapt to describe, review,
document, teach, or compare it ("built on OpenAdapt", "compatible with
OpenAdapt", "a plugin for OpenAdapt"), as long as the use is truthful and
does not suggest sponsorship or endorsement by MLDSAI Inc.
- Unmodified redistribution: distributing unmodified official OpenAdapt
packages under their original names, as package managers do.
- Forks for development: keeping the repository name when forking on GitHub
for the purpose of contributing back.

## What requires permission

- Using "OpenAdapt" or a confusingly similar name (for example
"OpenAdaptPro", "OpenAdapt2", "OpenAdaption") in the name of your own
product, service, company, domain name, or package.
- Publishing a modified fork under the OpenAdapt name in a way that users
could mistake for an official release. If you ship a modified version,
give it your own name and state that it is derived from OpenAdapt.
- Using the OpenAdapt logo, or a modified version of it, as your own mark or
in a way that implies affiliation, sponsorship, or endorsement.
- Merchandise bearing the name or logo.

## Contact

For permission requests or questions about this policy, email
hello@openadapt.ai.

This policy is intended to protect users from confusion about what is and is
not official OpenAdapt software. It follows common open source trademark
practice and does not restrict any rights granted by the MIT License to the
code itself.
125 changes: 125 additions & 0 deletions docs/platform-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# OpenAdapt platform release manifest

`platform-manifest.json` at the repository root is the single authoritative,
machine-readable statement of what constitutes an OpenAdapt platform release:
which launcher, flow, capture, and desktop versions belong together, where
their published artifacts live (with sha256 digests), which operating systems
and substrate drivers are supported, what qualification evidence backs the
release, and what release channel it is on.

## Why it lives here

This repository (`OpenAdaptAI/OpenAdapt`) is the launcher/meta-package: it is
the one place that already pins `openadapt-flow` and the other components via
`pyproject.toml`, and it is the integration surface users install. The
platform manifest is therefore generated and versioned here, next to the pins
it must agree with. Other repositories contribute source data only:

- PyPI is the authority for published versions, artifact URLs, and digests.
- `https://openadapt.ai/status.json` (maintained in `openadapt-web`,
`public/status.json`) is the authority for substrate availability, release
channel, and the public qualification summary.
- `openadapt-flow` holds the versioned qualification evidence packs the
manifest points at (`public-demo/evidence-packs/*/manifest.json` and the
effectbench task pack manifest).

## How it is generated

```bash
python scripts/generate_platform_manifest.py
```

The generator reads the real published state (PyPI JSON API, the live
status.json) plus this repository's `pyproject.toml`. It never invents
numbers: if a source is unreachable, empty, or disagrees with the repository,
it fails loudly. If a launcher release train is in flight (pyproject.toml
ahead of PyPI), pass `--allow-unreleased-launcher`; the manifest still records
the published version.

Regenerate the manifest after each component release and commit the result.

## How it is validated

```bash
python scripts/validate_platform_manifest.py
```

CI runs this on every pull request and on a schedule
(`.github/workflows/platform-manifest.yml`), so drift between the committed
manifest and the actually published artifacts fails loudly.

Validated today:

- Structure: manifest kind, schema major version, required fields, artifacts
with sha256 digests per component.
- Signature honesty: while `signature.value` is null, `signature.status` must
read `unsigned (signing infrastructure pending)`. A non-null signature
value fails validation because no verification path exists yet.
- Repo agreement: launcher version and openadapt-* compatibility ranges match
`pyproject.toml`.
- Published-artifact agreement: component versions, artifact filenames, URLs,
and sha256 digests match PyPI exactly (`--offline` skips this class).
- Status skew: disagreement with `status.json` versions is a warning by
default (status.json regenerates on its own cadence in `openadapt-web`);
`--strict-status` escalates it to a failure.

Not validated yet (planned):

- Cryptographic signature verification (see the signing plan below).
- Desktop OS installer artifacts (MSI/DMG); today only PyPI artifacts exist.

## Schema (v1)

Top-level fields:

| Field | Meaning |
|-------|---------|
| `manifest_kind` | Always `openadapt-platform-release-manifest`. |
| `schema_version` | Semver of this schema; validators reject unknown majors. |
| `generated_at` | UTC timestamp of generation. |
| `release_channel` | Lowercased product lifecycle from status.json (currently `beta`). |
| `components` | `launcher`, `flow`, `capture`, `desktop`: package name, published version, `requires_python`, and artifacts (`type`, `filename`, `url`, `sha256`). |
| `compatibility` | The launcher's supported Python range and its real openadapt-* dependency specifiers, extracted from `pyproject.toml`. |
| `supported_os` | Operating systems the launcher supports. |
| `substrate_drivers` | Substrate table (name, public label, delivery) read from status.json. |
| `qualification_evidence` | Stable evidence IDs pointing at the public status document and the flow evidence-pack manifests. |
| `signature` | See below. |

## Signing plan

The `signature` block is present in the schema from day one, but it is
honestly null:

```json
{
"algorithm": null,
"value": null,
"status": "unsigned (signing infrastructure pending)",
"plan": "docs/platform-manifest.md#signing-plan"
}
```

We deliberately do not fake a signature or stand up theater around one. The
intended rollout, in order:

1. Sigstore (cosign, keyless via the GitHub Actions OIDC identity) signing of
`platform-manifest.json` itself at release time, with the verification
step added to `validate_platform_manifest.py` before any signature is
ever emitted. Note that PyPI publish attestations (PEP 740, also
sigstore-backed) already exist for recent `openadapt-desktop` uploads and
provide per-artifact provenance independent of this manifest.
2. Windows Authenticode signing for desktop installers (MSI/EXE) once those
installers are produced.
3. Apple Developer ID signing plus notarization for macOS app and DMG
artifacts.

Until step 1 lands, the validator enforces that the manifest claims nothing:
a manifest with a non-null `signature.value` fails validation.

## Consumers

Anything that needs "what is the current OpenAdapt platform release" as data
should read this manifest rather than scraping PyPI or hardcoding versions:
release notes tooling, the website, the desktop updater, and support
tooling. Consumers must check `schema_version` and must treat
`signature.status` as informational until signing ships.
Loading