All walkthroughs / Health organisation
A researcher who holds a lease makes a new dataset from a query. Munitas runs the query itself, checks the result, and keeps the new dataset exactly as restricted as the data it came from. A colleague without access is refused, a query that reaches too far is stopped, and when the custodian withdraws the access the new dataset closes too.
me is the client library signed in as the person named on the label, and custodian is the client library signed in as the custodian.Sam, a researcher, only needs the older patients with a long-term heart condition. Munitas lets a person make a new dataset by writing a query, which is a question about tables written in a language called SQL, over datasets the person may already read. Munitas runs the query itself, not the person's computer, and holds the new dataset to the same rules as the data it came from. Sam holds a lease, which is permission from the custodian to read one dataset for a stated purpose and a limited time, and it is still in force.
Sam writes a query that joins the restricted admissions with the public diagnosis lookup and keeps patients over 65 with a chronic condition, then asks Munitas to plan it. Nothing runs yet. Munitas replies with the columns the new dataset would have, and the sensitivity label each one carries: none for a column that does not identify anybody on its own, quasi for a detail that could help identify a person when combined with others, and phi for protected health information, which means facts about a person's health.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam.sql = """SELECT a.admission_id, a.age, a.diagnosis_code, d.description, d.chronic,
a.length_of_stay_days, a.readmitted_30d
FROM a JOIN d ON d.code = a.diagnosis_code
WHERE a.age > 65 AND d.chronic"""
draft = me.draft(inputs=[{"dataset": "admissions", "alias": "a"}, {"dataset": "diagnosis_codes", "alias": "d"}], sql=sql, name="chronic-heart-patients-over-65-1012-1259",
primary_key=["admission_id"], purpose="readmission study")
for f in draft["fields"]:
print(f"{f['name']:<22}{f['type']:<8}{f['sensitivity']}")
print("The new dataset would be class", draft["output_class"])What came backadmission_id string none age int none diagnosis_code string none description string none chronic bool none length_of_stay_days int none readmitted_30d bool none The new dataset would be class RAW
After Sam confirms, Munitas copies the input files into a sandbox, an isolated container with no network connection and no passwords. The query runs there, and Munitas checks every row of the result against the plan. The query never runs on Sam's computer. Sam's session only waits for the answer.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam. To run the query, Munitas starts it in a sandbox, an isolated container with no network, and writes the result back as a new dataset.Names used here that are not defined in this box: draft was set in step 1.confirmed = me.confirm(draft)
result = me.wait(confirmed["id"])
print("Status:", result["status"])
print("New dataset:", result["target_name"], "|", result["output_class"])What came backStatus: succeeded New dataset: chronic-heart-patients-over-65-1012-1259 | RAW
The new dataset belongs to the same department as the data it came from, so Hartley, the custodian of that department, is still the person who decides who may read it. Sam made it, so Sam can read it straight away without asking anybody.
Munitas sealed the result, which means it is finished and closed for good, so it can never be edited. It is also an Iceberg table, an open way of storing a table that tools such as DuckDB read directly. Sam queries it.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam. DuckDB runs in this Python session. It asks Munitas for the table. If Munitas allows it, DuckDB receives a short-lived key and reads the table directly.Names used here that are not defined in this box: token is the token requested at the start of the same session.con = me.attach_duckdb(token)
print(con.sql("""SELECT admission_id, age, description, length_of_stay_days, readmitted_30d FROM lake."chronic-heart-patients-over-65-1012-1259".v1 ORDER BY age DESC LIMIT 5"""))
print(con.sql("""SELECT count(*) AS patients, round(avg(age), 1) AS mean_age, round(avg(length_of_stay_days), 1) AS mean_stay_days FROM lake."chronic-heart-patients-over-65-1012-1259".v1"""))What came back┌──────────────┬───────┬──────────────────────────────────┬─────────────────────┬────────────────┐ │ admission_id │ age │ description │ length_of_stay_days │ readmitted_30d │ │ varchar │ int64 │ varchar │ int64 │ boolean │ ├──────────────┼───────┼──────────────────────────────────┼─────────────────────┼────────────────┤ │ ADM-0383 │ 96 │ Coronary artery disease │ 7 │ true │ │ ADM-0172 │ 96 │ Atrial fibrillation, unspecified │ 2 │ false │ │ ADM-0038 │ 96 │ Heart failure, unspecified │ 6 │ false │ │ ADM-0220 │ 95 │ Atrial fibrillation, unspecified │ 7 │ true │ │ ADM-0286 │ 95 │ Essential hypertension │ 4 │ true │ └──────────────┴───────┴──────────────────────────────────┴─────────────────────┴────────────────┘ ┌──────────┬──────────┬────────────────┐ │ patients │ mean_age │ mean_stay_days │ │ int64 │ double │ double │ ├──────────┼──────────┼────────────────┤ │ 106 │ 77.2 │ 4.8 │ └──────────┴──────────┴────────────────┘
Every version of a dataset records what produced it: the step, who ran it, and the exact versions of the datasets it was made from.
Anybody auditing the new dataset can follow it back. Munitas keeps the purpose, the datasets and versions it was made from, and the text of the query.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam.Names used here that are not defined in this box: result was set in step 2.record = me.derivation(result["id"])
print("Purpose :", record["purpose"])
print("Inputs :", ", ".join(f"{i['dataset']} version {i['version']} ({i['class']})" for i in record["inputs"]))
print("Query :")
print(record["sql"])What came backPurpose : readmission study
Inputs : admissions version 1 (RAW), diagnosis_codes version 1 (PUBLISHED)
Query :
SELECT a.admission_id, a.age, a.diagnosis_code, d.description, d.chronic,
a.length_of_stay_days, a.readmitted_30d
FROM a JOIN d ON d.code = a.diagnosis_code
WHERE a.age > 65 AND d.chronicA new dataset is only safe if nobody can use it to get around the rules. This part tests that with a colleague, with a query that reaches too far, and with the custodian taking the access back.
Devi is a data engineer in the same organisation, with no lease on the new dataset. The new dataset is as restricted as the data it came from, so Devi cannot open it.
colleague is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Devi. DuckDB runs in this Python session. It asks Munitas for the table. If Munitas allows it, DuckDB receives a short-lived key and reads the table directly.colleague_token = colleague.catalog_token(purpose="curiosity")
other = colleague.attach_duckdb(colleague_token)
try:
print(other.sql('SELECT count(*) FROM lake."chronic-heart-patients-over-65-1012-1259".v1'))
except Exception as exc:
print("Refused:", exc)What came backRefused: HTTP Error: GetTableInformation endpoint returned response code Forbidden_403 with message "no role reaches class RAW"
no role reaches class RAW. Working in the same organisation is not enough.A query may read the datasets it names and nothing else. Sam tries one that reads a file from the machine instead.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam.Names used here that are not defined in this box: MunitasError is the error the client library raises when Munitas refuses a request.try:
me.draft(inputs=[{"dataset": "admissions", "alias": "a"}], sql="SELECT content AS admission_id FROM read_text('/etc/passwd')",
name="chronic-heart-patients-over-65-1012-1259-escape", primary_key=["admission_id"], purpose="readmission study")
except MunitasError as exc:
print("Refused:", exc)What came backRefused: HTTP 422: that query reaches outside the datasets it declared (a file or an address). A query may read only its declared inputs
The decision log lists every request to read data. Each request is recorded twice: once for whether it was allowed, and once for whether access was actually given. Here the log is filtered to the requests that were refused.
Hartley ends the lease. Sam immediately loses the admissions table, and the new dataset too, because Sam's access to the new dataset rested on the same lease.
custodian is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Hartley.Names used here that are not defined in this box: lease is the lease the custodian approved earlier in the same session.custodian.revoke(lease)
print("The lease is withdrawn.")What came backThe lease is withdrawn.
me is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Sam. DuckDB runs in this Python session. It asks Munitas for the table. If Munitas allows it, DuckDB receives a short-lived key and reads the table directly.Names used here that are not defined in this box: token is the token requested at the start of the same session; time is Python’s standard time module.time.sleep(3)
con = me.attach_duckdb(token)
for name in ["admissions", "chronic-heart-patients-over-65-1012-1259"]:
try:
print(con.sql(f'SELECT count(*) FROM lake."{name}".v1'))
except Exception as exc:
print(f"{name}: refused, {str(exc).split('message ')[-1]}")What came backadmissions: refused, "lease 881ec05f-a15b-4772-b131-bb94e674e16b revoked; no role reaches class RAW" chronic-heart-patients-over-65-1012-1259: refused, "lease 692021a3-f684-47b1-9fd8-503edebfdc5d revoked; no role reaches class RAW"
The new dataset is still listed, so Sam can still know that it exists, but Sam can no longer read it.