MunitasFeature tourAll walkthroughs

All walkthroughs / Finance organisation

Finance organisation · Governed datasets

Make a new dataset from card transactions

An analyst in a finance organisation is stopped at a restricted table of card transactions, asks for access, and is answered by the custodian of the department that owns it. The analyst then makes a new dataset from a query. The platform runs the query itself, checks the result, and keeps the new dataset exactly as restricted as the data it came from. When the custodian withdraws the access, the new dataset closes too.

20Steps
3People
about 16Minutes to read
Who is involved
Omar
Analyst in the Finance organisation
Marcus
Data custodian for the Fraud Operations department
Lena
Data engineer in the Finance organisation

How to read this page

Words used on this page

Organisation
A company or hospital group that uses Munitas. Each organisation sees only its own data.
Department
A team inside an organisation that owns some of its data, such as Cardiology.
Dataset
A named collection of records, such as a table of patient admissions.
Custodian
A person who holds the data custodian role, which allows deciding who may read data. A custodian decides for the departments they are an approver for.
Access level
How restricted a dataset is. Raw is the most restricted, and nobody may read it without permission. Published is the most open.
Lease
Permission, given by the custodian, to read one dataset for one stated purpose and for a limited time.
Query
A question about a dataset, written in a language called SQL.
Sensitivity label
How identifying a column is. none does not identify anybody, quasi is a detail that could help identify a person when combined with others, and phi is protected health information.
Token
A short-lived password that also states why the person wants to read.
Sealed
Finished and closed for good, so that it can never be edited.
Iceberg table
An open way of storing a table that tools such as DuckDB can read directly.
Claim
A statement, made by the person who brings a dataset in, about how sensitive it is. A different approver of the owning department has to confirm it.
Version
One sealed state of a dataset. Changing the contents means making a new version, never editing an old one.
Container
An isolated environment, with no network access of its own, where Munitas runs an agent.
Filter
Keeping only the rows of a table that match values the custodian names, such as one patient’s id, so that other people’s rows do not leave with them.
PART ONE

A closed table, and a request for access

The Fraud Operations department of a finance organisation owns a dataset of card transactions. Every record names the account holder and the last four digits of the card, so the dataset is closed to most people. An analyst wants to review it. This part shows the analyst being stopped, asking for access, and being answered by a different person.

1
Omar
Omar connects a tool and sees one open table
Python on Omar’s own computer, using the Munitas client library and DuckDB

Omar is an analyst in the Finance organisation. To start, Omar asks the platform for a token, which is a short-lived password that also says why Omar wants to read, here a cross-border fraud review. Omar then connects DuckDB, a free tool for analysing tables, to the platform's catalog, which is the list of tables the platform offers, and asks what is in it. DuckDB runs inside Omar's own Python session and reads the tables from the platform.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar. 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.
token = me.catalog_token(purpose="cross-border fraud review")   # shown once, never printed
import duckdb
con = duckdb.connect()
con.execute("INSTALL iceberg; LOAD iceberg;")
con.execute(f"ATTACH 'finance' AS lake (TYPE ICEBERG, ENDPOINT 'http://localhost:8000/iceberg', TOKEN '{token}')")
print(con.sql("SELECT schema AS dataset, name AS version FROM (SHOW ALL TABLES) ORDER BY 1"))
What came back
┌───────────┬─────────┐
│  dataset  │ version │
│  varchar  │ varchar │
├───────────┼─────────┤
│ merchants │ v1      │
└───────────┴─────────┘
Only one dataset is listed, merchants, a public list of merchants and their categories. The transactions dataset does not appear at all, because Omar may not read it.
2
Omar
The console shows why that table is open
Munitas console · Datasets

The console is the platform's web page for people. Signed in as Omar, the datasets list shows merchants, the public list of merchants, which the Risk and Compliance department owns.

Screenshot: The console shows why that table is open
The You can read column says 1 of 1, and the access level is Published, the most open level. Omar may read this dataset without asking anybody.
3
Omar
The restricted table is closed, with a reason
Python on Omar’s own computer, using DuckDB

The transactions dataset is at the Raw access level, the most restricted one. Nobody may read it without a lease, which is permission from the owning department's custodian to read it for one stated purpose and for a limited time. Omar tries to read it anyway.

Typed by Omar, in Python on Omar’s own computerDuckDB 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: con was set in step 1.
try:
    print(con.sql('SELECT count(*) FROM lake."transactions".v1'))
except Exception as exc:
    print("Refused:", exc)
What came back
Refused: HTTP Error: GetTableInformation endpoint returned response code Forbidden_403 with message "no role reaches class RAW"
The refusal ends with no role reaches class RAW. Omar's role, analyst, reaches the Published level and nothing above it. The platform gives the reason instead of a bare error. The line also names one lease that was revoked earlier, because this example was recorded more than once on the same data and the platform remembers Omar's earlier lease. Only the most recent revoked lease is named, however many there have been.
4
Omar
The console shows the same closed table
Munitas console · Datasets

The same datasets list in the console now shows the transactions dataset, which the Fraud Operations department owns and which holds the card transactions. The list also holds high-value-foreign-transactions, a dataset made from these transactions in an earlier recording of this walkthrough.

Screenshot: The console shows the same closed table
The You can read column says 0 of 1 on both rows, and the access level is Raw. Each dataset has one version, and Omar may read none of either.
5
Omar
Omar asks the Fraud Operations custodian for access
Munitas console · A dataset's page

On the dataset's page, Omar writes what the data will be used for, why something less sensitive would not do, and for how long access is needed. The request goes to Marcus, the custodian of the Fraud Operations department, because Fraud Operations owns the data.

Screenshot: Omar asks the Fraud Operations custodian for access
Under Your access the page says that the data is more sensitive than the person’s role reads and names who decides. It asks “What will you use it for?” and “Why do you need this rather than something less sensitive?”, and offers “For how long?” as four choices, 4 hours, 1 day, 3 days and 7 days. Under the form, Access ends by itself. Nobody has to remember to take it away.
6
Marcus
Marcus sees the request in the Fraud Operations queue
Munitas console · The custodian's home page

Marcus's home page lists the requests to read Fraud Operations' data. Only an approver of Fraud Operations can decide this one, and Omar cannot approve a request made under Omar's own name.

Screenshot: Marcus sees the request in the Fraud Operations queue
The first card reads Omar (Analyst) wants to read transactions v1, followed by Omar's reason and two buttons, Grant access and Refuse. The small text under them says what granting means: a fixed time, this purpose only, and it ends by itself. The counter says 2 because the queue also holds a request from Omar for a different dataset, card-transaction-log, which this walkthrough does not touch.
7
Marcus
Marcus grants the request
Munitas console · The custodian's home page

Marcus chooses Grant access. The request leaves the queue, and a lease now exists that lets Omar read this one dataset for the stated purpose.

Screenshot: Marcus grants the request
A notice at the bottom right reads Access granted to Omar (Analyst), and the box Currently granted is one higher than it was before the click. The newest entry under What you have decided says Granted, shows when the lease runs out, and offers a Revoke link, which Marcus uses at the end of this walkthrough.
8
Omar
With the lease, the restricted table opens
Python on Omar’s own computer, using DuckDB

Back in Omar's Python session, the kind of question that was refused a moment ago now works. Every name in the table is invented for this example.

Typed by Omar, in Python on Omar’s own computerDuckDB 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: con was set in step 1.
print(con.sql("""SELECT txn_id, account_holder, card_last4, amount, country FROM lake."transactions".v1 LIMIT 3"""))
What came back
┌─────────┬────────────────┬────────────┬────────┬─────────┐
│ txn_id  │ account_holder │ card_last4 │ amount │ country │
│ varchar │    varchar     │  varchar   │ double │ varchar │
├─────────┼────────────────┼────────────┼────────┼─────────┤
│ T-00001 │ Chidi Berg     │ 6344       │  182.4 │ GB      │
│ T-00002 │ Noor Dubois    │ 2082       │  24.01 │ GB      │
│ T-00003 │ Elif Fontaine  │ 4979       │  92.46 │ GB      │
└─────────┴────────────────┴────────────┴────────┴─────────┘
Real rows come back, including account_holder and card_last4, which identify a person and a card. That is why the dataset is restricted, and why anything made from it has to stay just as careful.
PART TWO

Making a new dataset from a query

Omar only needs the large payments made outside the home country. Munitas lets a person make a new dataset by writing a query over datasets the person may already read. The platform runs the query itself, not the person's computer, and holds the new dataset to the same rules as the data it came from.

9
Omar
The platform shows its plan before anything runs
Python on Omar’s own computer, using the Munitas client library

Omar writes a query that joins the restricted transactions with the public list of merchants and keeps payments over 300 made outside the United States, then asks the platform to plan it. Nothing runs yet. The platform replies with the columns the new dataset would have, and the sensitivity label each one must carry: none for a column that does not identify anybody, and quasi for a detail that could help identify a person when combined with others.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar.
sql = """SELECT t.txn_id, t.amount, t.country, t.occurred_at, m.category, m.high_risk, t.flagged
FROM t JOIN m ON m.merchant_id = t.merchant_id
WHERE t.amount > 300 AND t.country <> 'US'"""
draft = me.draft(inputs=[{"dataset": "transactions", "alias": "t"}, {"dataset": "merchants", "alias": "m"}], sql=sql, name="large-foreign-payments-1011-0019",
                 primary_key=["txn_id"], purpose="cross-border fraud review")
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 back
txn_id                string  none
amount                float   none
country               string  quasi
occurred_at           string  quasi
category              string  none
high_risk             bool    none
flagged               bool    quasi
The new dataset would be class RAW
category and high_risk, which come from the public list of merchants, are none. country, occurred_at and flagged, which come from the transactions, are quasi. The last line says the new dataset would be RAW, because it takes the strictest level among its inputs.
10
Omar
Data cannot be marked as less sensitive
Python on Omar’s own computer, using the Munitas client library

A label can be raised but not lowered. Lowering one would claim that the data is safer than the data it came from, and that claim needs somebody other than the person who wrote the query. Omar tries to mark the country column as none anyway.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar.Names used here that are not defined in this box: MunitasError is the error the client library raises when Munitas refuses a request.
probe = me.draft(inputs=[{"dataset": "transactions", "alias": "t"}], sql="SELECT txn_id, country FROM t", name="large-foreign-payments-1011-0019-probe",
                 primary_key=["txn_id"], purpose="cross-border fraud review")
try:
    me.confirm(probe, sensitivities={"country": "none"})
except MunitasError as exc:
    print("Refused:", exc)
What came back
Refused: HTTP 422: 'country' is computed from fields marked 'quasi', so it cannot be marked 'none'. A sensitivity can only be raised here: lowering one needs the agreement of somebody other than the person who wrote the query
The platform refuses and explains: country is computed from a field marked quasi, so it cannot be marked none.
11
Omar
Omar confirms, and the platform runs the query itself
Python on Omar’s own computer, using the Munitas client library

After Omar confirms, the platform copies the input files into a sandbox, an isolated container with no network connection and no passwords. The query runs there, and the platform checks every row of the result against the plan. The query never runs on Omar's computer. Omar's session only waits for the answer.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar. 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 9.
confirmed = me.confirm(draft)
result = me.wait(confirmed["id"])
print("Status:", result["status"])
print("New dataset:", result["target_name"], "|", result["output_class"])
What came back
Status: succeeded
New dataset: large-foreign-payments-1011-0019 | RAW
The status is succeeded, and the new dataset is RAW again. Omar did not choose that level: it follows from the datasets the query read.
12
Omar
The new dataset appears, owned by Fraud Operations
Munitas console · Datasets

The new dataset belongs to the same department as the data it came from, so Marcus is still the person who decides who may read it. Omar made it, so Omar can read it straight away without asking anybody.

Screenshot: The new dataset appears, owned by Fraud Operations
The You can read column says 1 of 1, and the access level is still Raw. The name ends in the month, day, hour and minute when it was made, so that a second run never meets a name that is already taken. The notebook half of this page was run a few minutes earlier, so the dataset made there carries an earlier time.
13
Omar
The new dataset opens like any other table
Python on Omar’s own computer, using the Munitas client library and DuckDB

The platform 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. Omar queries it.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar. 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 was set in step 1.
con = me.attach_duckdb(token)
print(con.sql("""SELECT txn_id, amount, country, category, flagged FROM lake."large-foreign-payments-1011-0019".v1 ORDER BY amount DESC LIMIT 5"""))
print(con.sql("""SELECT count(*) AS transactions, round(sum(amount), 2) AS total_amount, count(*) FILTER (WHERE flagged) AS flagged FROM lake."large-foreign-payments-1011-0019".v1"""))
What came back
┌─────────┬─────────┬─────────┬─────────────┬─────────┐
│ txn_id  │ amount  │ country │  category   │ flagged │
│ varchar │ double  │ varchar │   varchar   │ boolean │
├─────────┼─────────┼─────────┼─────────────┼─────────┤
│ T-00424 │ 1060.54 │ GB      │ dining      │ false   │
│ T-00170 │  801.76 │ BR      │ electronics │ false   │
│ T-00388 │  735.71 │ NG      │ dining      │ false   │
│ T-00580 │  705.47 │ DE      │ travel      │ false   │
│ T-00180 │  675.66 │ DE      │ jewellery   │ false   │
└─────────┴─────────┴─────────┴─────────────┴─────────┘

┌──────────────┬──────────────┬─────────┐
│ transactions │ total_amount │ flagged │
│    int64     │    double    │  int64  │
├──────────────┼──────────────┼─────────┤
│           31 │     15103.46 │       0 │
└──────────────┴──────────────┴─────────┘
The first answer lists the five largest payments, all above 300 and none in the United States. The second answer counts the matching transactions and adds up their amounts, from a dataset that holds no account holder names.
14
Omar
The console records where the new dataset came from
Munitas console · A dataset's page

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.

Screenshot: The console records where the new dataset came from
Your access reads You can read this until a date, for the stated purpose. Where this came from shows the step derive and two Made from versions, one for the transactions and one for the merchants.
15
Omar
The query is kept exactly as written
Python on Omar’s own computer, using the Munitas client library

Anybody auditing the new dataset can follow it back. The platform keeps the purpose, the datasets and versions it was made from, and the text of the query.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar.Names used here that are not defined in this box: result was set in step 11.
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 back
Purpose : cross-border fraud review
Inputs  : transactions version 1 (RAW), merchants version 1 (PUBLISHED)
Query   :
SELECT t.txn_id, t.amount, t.country, t.occurred_at, m.category, m.high_risk, t.flagged
FROM t JOIN m ON m.merchant_id = t.merchant_id
WHERE t.amount > 300 AND t.country <> 'US'
The inputs line names transactions version 1 (RAW) and merchants version 1 (PUBLISHED), which are the most restricted and the most open access levels. The query appears word for word.
PART THREE

The rules still apply to the new dataset

A 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.

16
Lena
A colleague without access is refused
Python on Lena’s own computer, using the Munitas client library and DuckDB

Lena 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 Lena cannot open it.

Typed by Lena, in Python on Lena’s own computercolleague is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Lena. 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."large-foreign-payments-1011-0019".v1'))
except Exception as exc:
    print("Refused:", exc)
What came back
Refused: HTTP Error: GetTableInformation endpoint returned response code Forbidden_403 with message "no role reaches class RAW"
The reason is the same as before: no role reaches class RAW. Working in the same organisation is not enough.
17
Omar
A query that reads outside its inputs is stopped
Python on Omar’s own computer, using the Munitas client library

A query may read the datasets it names and nothing else. Omar tries one that reads a file from the machine instead.

Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar.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": "transactions", "alias": "t"}], sql="SELECT content AS txn_id FROM read_text('/etc/passwd')",
             name="large-foreign-payments-1011-0019-escape", primary_key=["txn_id"], purpose="cross-border fraud review")
except MunitasError as exc:
    print("Refused:", exc)
What came back
Refused: HTTP 422: that query reaches outside the datasets it declared (a file or an address). A query may read only its declared inputs
The platform refuses with that query reaches outside the datasets it declared. Nothing ran.
18
Marcus
Every refusal is on record, with the reason
Munitas console · Access decisions

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.

Screenshot: Every refusal is on record, with the reason
The filter reads Refused, every row says no under Result, and the last column, Why, gives the reason in words, such as No role reaches class RAW. Each row also says what the request was for.
19
Marcus
Marcus withdraws access, and the new dataset closes too
Python on Marcus’s and Omar’s own computers, using the Munitas client library and DuckDB

Marcus ends the lease. Omar immediately loses the transactions dataset, and the new dataset too, because Omar's access to the new dataset rested on the same lease.

Typed by Marcus, in Python on Marcus’s own computercustodian is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Marcus.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 back
The lease is withdrawn.
Typed by Omar, in Python on Omar’s own computerme is the Munitas client library, a small Python package that asks Munitas for things on the person’s behalf, signed in as Omar. 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 was set in step 1; time is Python’s standard time module.
time.sleep(3)
con = me.attach_duckdb(token)
for name in ["transactions", "large-foreign-payments-1011-0019"]:
    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 back
transactions: refused, "lease bcc8bfb3-97b5-47ac-891d-fc4e58e4dcf9 revoked; no role reaches class RAW"
large-foreign-payments-1011-0019: refused, "lease fe97cb6c-7169-44ca-bf7f-cd679eae84fb revoked; no role reaches class RAW"
Both tables report refused, and the reasons name the revoked leases.
20
Omar
The console shows the new dataset closed again
Munitas console · Datasets

The new dataset is still listed, so Omar can still know that it exists, but Omar can no longer read it.

Screenshot: The console shows the new dataset closed again
The You can read column now says 0 of 1, where it said 1 of 1 earlier.