v1.2.0rc2
Pre-release
Pre-release
·
6 commits
to develop
since this release
- Storage moves from YAML to SQLite. Every declaration — the app, the
parties, data overrides and reviews, touchpoint declarations, stores,
activities, threat stamps, the findings register, custom vocabularies
and actors — lives in onecompliance.dbat the repository root; the
compliance/folders are gone andsnow.yml'scompliance:block only
names the discovery engine (diris dropped). The database is tuned to
diff well (WAL, no auto-vacuum, a checkpoint on close) andinitadds
its transient files to.gitignore. Cross-references (hosts, data refs,
transfers, recipients, stamps, locks) are normalised tables queried with
SQL rather than loaded and joined in Python. No migration from the YAML
layout: it is a new system. - The
CODEOWNERSstep ofinit(and itscheckdiagnostic and flags)
is removed. - Marker subjects are
<record>#<field>(parties/acme#address,
activities/ordering#retention,app#description) instead of file
names. - The library takes the repository from a process-wide container set
once per command (--root); noroot=/shared=arguments. - Duplicate parties are refused.
party_add(andinit) compare the
new party with every declared one — normalised name (case, accents,
punctuation, legal forms and a typo or an extra word ignored), shared
registrable domain or setting name of the website /hosts, respelled
id — and fail with the matching id when one looks the same. A party
that names a store of the project (Sentry, "SMTP mail server", a
host ofEMAIL_HOST) is refused too: that is a store write. The
refusal is final for agents;distinct_fromis set by a human only.
checkreports coexisting lookalikes as aparty-duplicateerror. - Duplicate stores are refused the same way:
store_addcompares the
new store with the unit's visible ones (shared host / settings name,
alike name, slug or config backend) and fails with the slug to reuse;
checkreports coexisting lookalikes asstore-duplicate. partiesCLI:list,show,add,set,remove [--force],
merge ... --into,to-store <id> <unit:slug>(a party that was
infrastructure becomes store writes),to-flow <id>(a party that was
the project itself: its transfers go, the derived call stays),
distinct,duplicates.- Reach: who the code lets in, apart from who it serves. A finding's
likelihood used to come from the touchpoint'sscope, so asystem
webhook listing whoseget_permissions()returned[]on the leader —
readable by anyone on the internet — scoredinfo(system, 0.01)
instead ofhigh(anonymous). Touchpoints now carry areach
(anonymous | subject | staff | system), inferred from the auth facts
alone (no auth is anonymous whatever the scope says) and declared by the
reviewer throughtouchpoint_set_data(reach=). The Django introspection
reportsauth_custom— DRF views overridingget_permissions,
get_authenticators,dispatch… or using a hand-written authentication /
permission class — and such a touchpoint is pending until a reviewer
reads the override and declares its reach; the declaration is refused
without it. Schema v4 addstouchpoints.reach. - Severity buckets rebalanced.
criticalis an open buffet — anyone
(or any account onto every other account's data) takes personal data in
bulk, a confidential listing or a special-category record;highis
what takes some work, or an account, to get at what is not yours;
mediumis what someone could do that they should not. Concretely: the
bulkdegree weighs 2 (each degree doubles the last), critical starts
at 4, high at 2, medium at 1; every reviewable threat in_mapping.yaml
declares aneffort(openwalk in ×1,worka script or payload ×¾,
chainanother flaw or a victim ×½) that scales the likelihood, so a
brute force on a login or a stored XSS ishigh, notcritical; the
ownership threats (AA03, AC01, AC07, AC12, DS05) arehorizontal— a
subject who can read every other subject's rows weighs like an anonymous
caller; the oracle threats (DS01, INP18) are capped at oneattribute
and fingerprinting (DS03, HA03) atexistence, whatever the touchpoint
lists; escalation weighs like a disclosure of the data the touchpoint
handles (floor 2) instead of a flat 4, so an unauthenticated webhook that
creates one record ishighand an unauthenticated confidential listing
critical; a tampering on a touchpoint that only creates is one
record; denial of service is a flat 1 whatever the data behind the
endpoint (an outage is an incident, not a breach), so an unthrottled
public endpoint ismedium, nothigh. A touchpoint's write into an internal store of the project
(own_store_write: database, files, cache, queue, search, realtime) no
longer carries the leak cells DS06/DR01 — the response to the caller and
the mail / monitoring / external flows keep them. - Reach is derived through the front. A SvelteKit route with no auth
of its own takes the reach of the api touchpoints itcalls(the
cookie is forwarded: whoever the api lets in) and of a parent
+layout.server.tsthat redirects unauthenticated callers to login (the
introspection reports such aloadas alogin guardauth fact). The
api.bizneo(fetch).opId()client-factory call style is now recognised,
and DRF routes carry drf-spectacular's operation ids, so front routes
link to the DRF endpoints they proxy. auto-reviewnarrates each write once: the line comes from the activity
log the MCP server appends to, no longer also from the tool-call stream
(activities, parties and declarations were printed twice). Stores,
undeclared-flow reports and challenges get a line too.- A reported sink that names a store the way the party guard sees it
(sdk-sentry-sdk, "Sentry error tracker", "the mail server") resolves
to that store, so declaring the store write closes the report. Before,
the sink stayed an unresolvable transfer:party_addrefused the party
(it IS the store) and the touchpoint could never leave pending. - A relative
fetch("/api/x")in a SvelteKit route is linked to the
route of the project that serves it (acallsedge, like a generated
client call) and leaves the outboundfetches: the project's own
endpoints never show up as an unknown host to declare a party for.
storesgainsadd,set,remove,merge,distinct. - Outgoing email and error monitoring are built-in stores. Every
Django unit has amail-defaultstore (typemail) claiming
EMAIL_BACKEND/EMAIL_HOST, and anerrors-sentrystore (type
monitoring) claimingsentry_sdk/SENTRY_DSNwherever the SDK is
installed; asend_mail/EmailMessage/email_usercall, or a
capture_exception, is a write to them. How mail is sent and where
events land (cloud, self-hosted) is infrastructure, so no distinction is
made between backends: no moresmtp-*stores or Sentry / mail provider
parties declared by agents. load_declarationsreads the parties even when theapprow is
missing, soparties listworks beforeinit.- Fix: the unit's introspection no longer inherits
VIRTUAL_ENV/
PYTHONPATH/ uv and poetry variables from the process running
model-wtf. Underuv run model-wtf,poetry runin the unit honoured
ourVIRTUAL_ENVand imported the project in model-wtf's venv
(No module named 'celery'), anduv runcreated a stray.venvand
uv.lockin the unit. - Schema migrations:
compliance.dbcarries its schema version and is
upgraded step by step (model_wtf.compliance.migrations) when a newer
release opens it; a file from a newer release is refused.