Repository navigation
Releases: diagnos-tech/integration
Release list
diagnos 0.1.0
0.1.0 is the first version of diagnos ever published — see
MIGRATING.md if you are coming from an internal or from-source checkout of the former imgexam
monorepo.
Added
-
One package,
diagnos, with extras.pip install diagnosis the base: no dependencies at all, and four
commands —diagnos docs(Help Center, Developer Central, Trust Center),diagnos version(version, interpreter,
platform and installed extras,--jsontoo),diagnos setupanddiagnos help.diagnos[sdk]adds the SDK
(from diagnos import Diagnos),diagnos[cli]the full command line,diagnos[api]the REST API (diagnos-api);
openbaoanddev-certssit on top.import diagnosworks on the base install, and a missing extra raises
MissingExtraErrornaming the command that adds it.diagnos-clianddiagnos-apiare no longer separate
packages;MIGRATING.mdmaps the old names. -
diagnos setup sdk | cli | api: installs one layer step by step — the SDK into your project withuv add, the CLI
into the environment that runsdiagnos(or as auv tool), the API as its signed image with Docker. Every
command is shown first and confirmed once (default no);--dry-runand--yesfor scripts. The API image is
verified by the pulled digest with cosign (3.1.3+) against the release workflow's identity, and thedocker runis
printed by that digest. A missinguvor Docker exits with69and its install page — nothing is ever installed
behind your back. -
wrappers/contract.json: the language-neutral contract of the four base commands, for wrappers in other
languages; the Python base is tested against it. -
Signed releases: every wheel, the sdist and
SHA256SUMScarry a cosign keyless signature and a SLSA provenance
attestation; PyPI gets PEP 740 attestations through Trusted Publishing; the GitHub release attaches every file,
bundle and the commands that verify them (SECURITY.md). -
Images on GHCR, for
linux/amd64andlinux/arm64:ghcr.io/diagnos-tech/diagnos-apiand
ghcr.io/diagnos-tech/diagnos(the CLI), built from the released wheels, with SBOM and provenance, signed by
digest. They run as uid10001with root-owned, read-only code and no pip or uv. -
Renamed the project from
imgexamtodiagnos, end to end: packages (imgexam→diagnos,imgexam-cli→
diagnos-cli,imgexam-api→diagnos-api), modules (imgexam→diagnos,imgexam_cli→diagnos_cli,
imgexam_api→diagnos_api), the public class (Imgexam→Diagnos) and exceptions (ImgexamError→
DiagnosError,ImgexamPermissionError→DiagnosPermissionError), everyIMGEXAM_*environment variable to
DIAGNOS_*, the CLI command (imgexam→diagnos), the Docker image (imgexam-api→diagnos-api), and the
default vault URL tohttps://vault.diagnos.health. SeeMIGRATING.mdfor the full list,
including what was deliberately left unrenamed (theimgexam-*-v1cryptographic labels and the OpenBao static
seal key id). -
Consumer-driven Pact contract test suite (
contracts/) between the SDK and the vault:make contract/
make contract-check, a deterministic, normalizedcontracts/diagnos-sdk-diagnos-vault.json, and provider states
covering the clock, enrollment (pending, approved, denied, forgotten), lock, patients, exams and files. See
contracts/README.md. -
Makefileas the single entry point for every check a contributor or CI runs (sync,fmt,lint,types,
test,cov,contract,contract-check,check). -
GitHub Actions:
CIon every pull request (lint,mypy --strict, the docs, the Pact contract, the Rust enclave,
every Python suite on CPython 3.11 and 3.13 with a coverage floor, and zizmor and actionlint over the workflows),
behind one required check;Packageon pull requests intomain;Releaseonv*tags onmain; monthly
grouped Dependabot updates intodevelop, whose minor and patch updates merge on their own once CI passes; and a
weekly OpenSSF Scorecard. -
A self-hosted coverage badge, published to the orphan
badgesbranch byCIon every release. -
Bilingual (🇺🇸/🇧🇷) documentation across the repository, enforced by
scripts/check_docs.py(every document ships
asNAME.md+NAME.pt-BR.md) andscripts/check_bilingual.py(every module, class and public function's
docstring, Python and Rust). -
Repository layout: the three packages live under
apps/(apps/sdk,apps/cli,apps/api);contracts/,
docs/andscripts/stay at the root. -
update(..., expected_latest_version_id=...)(CLI--expect-version): the vault refuses the write if another
version was saved since. Reservations that meet another writer's pending version are retried briefly (1.5 s, 3 s),
and commits lost to the network or a5xxare replayed with backoff. -
Patient
tags(sealed list labels):create(..., tags=[...]),update(..., tags=...)(Nonekeeps them), CLI
--tag. -
DIAGNOS_TIME_PRECISION/Settings.time_precision: truncate dates to the workspace's anonymization precision
before sealing, as the web app does;diagnos.truncate_timestampanddiagnos.to_iso_instantfor your own dates. -
ProtocolErrorfor a vault answer the protocol does not allow (e.g. a signed size that is not the body's); the API
answers it with502andcode: protocol_error. -
Folders:
Drive.create_folder(), andparent_id=onupload,upload_many,listanditer_all(CLI
files mkdirand--folder; APIPOST /v1/drives/{sg}/foldersandparent_id). -
Workspace-wide files:
vault.drives.list()/iter_all()across every group the session may list, filtered by
security_group,exam_id,parent_idandinclude_pending;vault.drives.get(),name_of(),download()and
iter_download()read any file by node id alone (CLIfiles listwithout--group,files get/downloadby id). -
DIAGNOS_STORAGE_HOSTS/Settings.storage_hosts: presigned storage URLs are followed only over HTTPS to an
allowed host or its subdomains — by default the user-content domaindiagnosusercontent.comand R2's
r2.cloudflarestorage.com— so ciphertext and SSE-C keys never go to a host nobody chose. -
CLI:
--groupis optional onpatients create,exams create,files uploadandfiles mkdir— it falls back to
DIAGNOS_GROUP, then (for an exam) the patient's group, then the session's only group, and says which it picked;
with several groups it stops and lists them. -
Patients.index(id)/Exams.index(id): a document's metadata (group, versions, flags) without opening its record. -
CLI session agent:
diagnos loginkeeps the session for the commands that follow, untildiagnos logout, the idle
timeout (DIAGNOS_AGENT_IDLE_MINUTES, 8 hours by default) orSIGTERM. The session stays in one background
process, in the SDK's locked memory; later commands send it their arguments over a private Unix socket and it runs
them, streaming output, prompts and exit codes back — the keys never leave that process, nothing is written to disk
and no OS keychain is used.DIAGNOS_AGENT=offkeeps every command in its own process.diagnos statusshows the
agent. -
CLI
diagnos logout: revokes the kept session, wipes its keys and stops the agent. -
CLI global options (
--json,--quiet,--no-color,--token,--vault-url) go anywhere on the line:
diagnos patients list --jsonworks. -
API
diagnos-api dev-certs [DIR]: a throwaway local CA, alocalhostserver pair and a client pair in one step
(keys0600, 30 days, never overwritten without--force; needs thediagnos[dev-certs]extra outside a
checkout).python -m diagnos.apiruns the API likediagnos-api. -
diagnos.UploadSource, exported at the top level. A.dcmfile is typedapplication/dicom, so the vault
classifies it.
Changed
- Patients and exams speak the vault's current protocol (
vault.patients,vault.exams, and the matching CLI
commands and API routes). A document belongs to exactly one security group (security_group=takes one id;
DocumentIndex.security_group_id); version state lives inDocumentIndex.streams(withlatest_version_id,
versionsandpending_version_idshorthands for thedatastream); patients' version routes carry
/streams/data; every version is sealed under a per-version key derived from the vault'ssecurity_context, as a
rawsalt ‖ nonce ‖ ciphertextbody; every write carries the sealed summary (encrypted_index). Pinned by vectors
sealed with the web app's own code and by 13 new contract interactions. PatientRecordandExamRecordnow mirror the web app's records:identifiers(vault-sealed),address,
race_identity;report_lexical/report_html,modality,exam_date.legal_id,ExamRecord.descriptionand
ExamRecord.reportare gone. Dates are stored the way the web app stores them (UTC ISO 8601 instants).- A misspelled record field (
birthdate) is refused with the field it resembles; any other unknown field is kept, so
a read-modify-write never drops a field the web app added. Validation errors on records never echo the value. list()returns rows with the decrypted summary (PatientListItem/ExamListItem); the CLI shows it with
--summaryand the API with?summary=true— rows stay anonymous by default.get()returns the newest content like the web app (a newer draft wins);include_draft=False/--committed
read committed versions only.- Archive and delete no longer re-upload the record: they are flag changes without a new version. New
restore()
(CLIrestore, APIPOST …/restore) takes a document out of the trash. Exams.createno longer takesmodality=: modality is a sealed record field; onlypatient_idis sent in clear.- Files speak the vault's current protocol (`...