Interlinked · Legal

Interlinked - The Cube

Privacy Notice

What this product records about a person, who decided to record it, how long it is kept, and how to get it back or have it destroyed.

Version 2026-09-11.1 In force from 2026-09-11 Operator see below

Not reviewed by a lawyer This document was drafted by the engineering team from the actual behaviour of this software. It has not been reviewed by a lawyer. It must be reviewed by a qualified Romanian lawyer before this product is sold to a paying customer. docs/LEGAL.md lists the specific points that review has to settle.

Start here: this product measures people

It is worth saying plainly rather than burying it. When a gallery sends you a viewing room, the room records which artworks you looked at, for how many milliseconds each, what you zoomed into, what you marked, and how many separate times you came back. Those numbers are combined into a percentage the gallery sees next to your name, which it uses to decide who to follow up with. That is the product. It is not an incidental analytics layer bolted onto something else.

Because of that, the measurement only ever starts after you have been told and have agreed. The screen before the first wall is not a formality: choosing BROWSE PRIVATELY opens no visit, sets no cookie and writes no measurement at all, and the rest of the room works normally. Choosing CONTINUE WITH VIEWING RECORD is what switches the recording on.

The consent you give is stamped with the version of this notice that was in force at that moment (currently 2026-09-11.1), stored alongside the timestamp. That is deliberate. A consent recorded as nothing but a date is a claim that somebody agreed to something, and the words they agreed to cannot be reconstructed once the text has been edited. A consent whose wording is unknown is not evidence of anything, so we record which wording it was.

Who is responsible

The gallery that sent you the room is the controller. It chose you, it chose the works, it decided that a viewing record would be kept and what it would be used for. If you want to know why you were sent a room, or you want the gallery to stop, the gallery is the party who decides. Its contact address appears at the bottom of the room and in any copy of your data we give you.

Solaas Tech S.R.L. is the processor. We built and run the software, we hold the data on the gallery's documented instruction, and we do not decide who gets measured. We do not sell data, we do not use it to train anything, and we do not combine one gallery's collectors with another's.

Solaas Tech S.R.L., a company registered in Romania, sole registration code (CUI) 54888013, trade register number J2026038411001, with its registered office at Str. Prieteniei nr. 7, bl. P, sc. B, et. 2, ap. 26, Constanta, Romania. Contact: stoiana00@gmail.com. Interlinked, the Cube is a project of the company named above.

We are the controller for a short and separate list: the gallery staff accounts and their sign-in sessions, keeping the service itself secure and working, and the record of data-rights requests described at the bottom of this page.

What is recorded about a collector

Every row below is a real column in this system. Nothing is listed that the software does not write, and nothing the software writes is left out.

Who the gallery sent a room to

WhatWhereDetail
Name recipients.name As the gallery typed it, or as the collector typed it into an inquiry form.
Email address recipients.email The address the viewing-room link was sent to, or the one given in an inquiry.
Company, city, country recipients.company / city / country Optional, gallery-supplied.
Opt-in timestamp recipients.opted_in_at When the gallery recorded the contact as opted in.
CRM counters contacts.rooms_sent / marks / inquiries Running totals the gallery sees on its contacts screen.

Why: Letting the gallery address a named person, and letting the platform deliver the link to them.

Lawful basis: The gallery decides who receives a room and on what basis (typically its legitimate interest in its own client relationship, Art. 6(1)(f), or consent it obtained itself). The operator processes it on the gallery’s documented instruction, Art. 28.

The link itself

WhatWhereDetail
Room token room_links.token The unguessable string in the URL. It is the credential for the room.
Expiry, maximum opens, opens used, revocation room_links.expires_at / max_opens / opens_count / revoked_at How long the link lives and how many visits it has been spent on.

Why: Controlling who can open a private room and for how long.

Lawful basis: Necessary to perform the service the gallery contracted for, Art. 6(1)(b) between operator and gallery; Art. 28 instruction as to the collector.

Each visit

WhatWhereDetail
Visit start, last activity, end room_sessions.started_at / last_seen_at / ended_at The sitting. One row per visit, not per page.
Consent moment room_sessions.consent_at The instant the collector pressed CONTINUE WITH VIEWING RECORD.
Which text was consented to room_sessions.consent_policy_version The version string of this notice as it stood at that instant.
Browser user agent room_sessions.ua The raw User-Agent header, stored verbatim.
Hashed IP address room_sessions.ip_hash Salted SHA-256 of the address, truncated to 32 hex characters. The address itself is never written down; the hash exists to count unique visitors and to recognise abuse.

Why: Recognising a returning visit, proving that consent was given and to which text, and telling one visitor from another without holding an address.

Lawful basis: Consent, Art. 6(1)(a), taken on the consent screen before anything is measured. A visitor who chooses BROWSE PRIVATELY is not measured at all.

What the person looked at, the measurement

WhatWhereDetail
Event type signal_events.type One of: open, consent, wall_view, work_view, zoom, mark, unmark, inquiry, close.
Which artwork signal_events.work_id The specific work the event was about, when it was about one.
Dwell, in milliseconds signal_events.dwell_ms How long the work or wall was on screen. Clamped to 10 minutes per event, so a forgotten tab cannot fabricate an evening of interest.
Timestamp signal_events.created_at Client-supplied, rejected if it is in the future or before the visit started.
Marks marks.work_id / created_at Which works the collector shortlisted, and when.

Why: Producing the gallery’s SIGNALS screens: views and average dwell per work, and a 0-100 "strength" score per named recipient computed from return visits, total dwell, marks, zooms and inquiries (src/lib/signals/strength.ts).

Lawful basis: Consent, Art. 6(1)(a). This is the part of the product that measures a named person, and it is the reason the consent screen is not skippable: nothing in this category is written for a visit that did not consent (`needsConsent()` in src/lib/signals/events.ts refuses it twice, at the route and at the writer).

What the person wrote

WhatWhereDetail
Inquiry text inquiries.message Up to 2000 characters, stored as plain text, never as markup.
Thread and messages threads.subject, messages.body / sender / created_at The conversation the inquiry opened in the gallery’s inbox, and everything said in it since.
Inquiry status inquiries.status new / replied / closed, as the gallery works it.

Why: Letting a collector ask about a work and letting the gallery answer.

Lawful basis: Steps taken at the request of the person before entering a contract, Art. 6(1)(b), and the gallery’s legitimate interest in keeping its own commercial correspondence, Art. 6(1)(f).

The follow-up draft, when a gallerist asks for one

WhatWhereDetail
What was sent not stored; assembled per request The works this person opened, with their own view counts, minutes, zoom-ins and marks; their strength score and visit dates; and their inquiry text truncated to 400 characters.
What was removed first src/lib/ai/facts.ts Name, email, company and city, including inside the free text of an inquiry, replaced by placeholders. The prompt is then read back and the request refused if any of them survived.
The draft not stored Shown to the gallerist with the real name substituted in here. It is not written to the database.

Why: Letting a gallerist ask for a first draft of a follow-up email. It happens on a button press, never on a schedule, never in a batch, and never on page load. The feature disappears entirely if no API key is configured.

Lawful basis: The gallery’s legitimate interest in following up its own enquiry, Art. 6(1)(f). Pseudonymisation is a safeguard, not a basis: we hold the key that turns the placeholder back into a person, so this is still personal data and still a transfer to a processor.

Data-rights requests

WhatWhereDetail
Request kind and status data_requests.kind / status export or delete; pending, verified, processing, done or rejected.
Address it was made from data_requests.email Needed to verify the request and to answer it.
Verification token hash data_requests.verification_token_hash SHA-256 of the token in the emailed link. The token itself is never stored.
Timestamps and outcome data_requests.created_at / verified_at / completed_at / note What was done, when, and what was kept and why.

Why: Being able to prove that a request was answered and within what time, the accountability duty of Art. 5(2). It is the one record an erasure request does not erase.

Lawful basis: Legal obligation, Art. 6(1)(c), read with Art. 5(2).

What is recorded about a gallery user

WhatWhereDetail
Email and name users.email / users.name What the account was registered with.
Password hash users.password_hash argon2id. The password itself is never stored and cannot be recovered.
Membership and role gallery_members.gallery_id / role Which gallery the user acts for, as owner, manager or staff.
Sign-in sessions sessions.id / expires_at / user_agent One row per signed-in device. `id` is the SHA-256 of the token, never the token itself.
Linked Apple / Google identity user_identities.provider / provider_user_id / email Only if the user chose to sign in with a provider.
Push notification devices push_devices.token / platform / user_agent Only if the user turned notifications on in the mobile app.

Why: Running the account, keeping it signed in, and letting it be recovered.

Lawful basis: Performance of the contract with the gallery, Art. 6(1)(b), and the operator’s legitimate interest in the security of the service, Art. 6(1)(f).

WhatWhereDetail
Plan and status subscriptions.tier / status Which plan the gallery is on and whether it is trialling, live, in grace or cancelled.
Stripe identifiers subscriptions.stripe_customer_id / stripe_subscription_id / stripe_price_lookup_key Pointers into Stripe. They are not billing details in themselves.
Billing period dates subscriptions.current_period_end / trial_ends_at / grace_until / canceled_at When the period ends, when the trial ends, and how long a failed payment is forgiven before anything is restricted.
Webhook event ids stripe_events.id So a repeated webhook is applied once. Nothing about a person.
Card details nowhere Never on this server. Checkout and the billing portal are pages Stripe hosts; the card is typed into Stripe, not into us.
VAT identification number held by Stripe Collected at checkout because customers are businesses, and because it decides whether the invoice carries Romanian VAT or a reverse charge.

Why: Taking payment for a subscription, issuing invoices, and applying the right tax treatment.

Lawful basis: Performance of the contract, Art. 6(1)(b), and a legal obligation for the invoicing and tax records, Art. 6(1)(c).

What we do not do

Who else sees it

Everything runs on one server we control, with its database on the same machine. These are the only parties data can reach beyond it.

WhoFor whatWhat reaches themWhere
Cloudflare Ltd. (R2 object storage)
only when configured
Storage of artwork images and their derivatives Artwork image files and the object keys that name them. No collector identifiers, no signal data. EU (bucket region WEUR)
[[SMTP_PROVIDER]] (email relay)
only when configured
Delivery of transactional email Recipient address and display name, the subject and body of the message, which includes viewing-room invitations, inquiry notifications, password resets and data-request verification links. [[SMTP_REGION]]
[[HOSTING_PROVIDER]] (server hosting) The virtual server the application and its PostgreSQL database run on Everything in this document, at rest and in memory. [[SERVER_LOCATION]]
Apple Inc. / Google Ireland Ltd.
only when configured
Identity providers, only for gallery users who choose "Sign in with Apple / Google" The sign-in exchange itself. They tell us a subject id and, sometimes, a verified address; we tell them nothing about rooms, collectors or signals. United States / Ireland
Anthropic PBC (Claude API)
only when configured
Drafting one follow-up email, when a gallerist presses COMPOSE FOLLOW-UP What a named collector looked at, without who they are. Sent: the gallery’s public details; for the works that person actually opened, the title, artist, year, medium, dimensions and status together with their own views, minutes, zoom-ins and marks against each; their strength score, session count, total minutes and visit dates; and the text of their inquiries truncated to 400 characters, with their name, company and city replaced by placeholders. Not sent: their name, email, company or city; any price; any image; any other collector or gallery; IP addresses, user agents, session tokens or link tokens; works they never opened. The real name is put into the finished draft here, after the model has written it. Pseudonymous is not anonymous: we hold the key, so this is still personal data and still a transfer. United States. This application does not pin the inference region.
Stripe Payments Europe, Ltd.
only when configured
Subscription payments, invoices and the billing portal. Galleries only. The billing contact and company details a gallery types into Stripe Checkout, including the VAT identification number collected from business customers, and the card itself. Card details never touch this server: checkout and the billing portal are pages Stripe hosts. What comes back and is stored here is a customer id, a subscription id, a price key and the billing period dates. No collector data of any kind reaches Stripe. Ireland, with onward transfers under Stripe’s own terms

Apple and Google appear on that list only for a gallery user who chooses to sign in with them, and that exchange is about the sign-in and nothing else. Where a provider is outside the EU, the transfer rests on the European Commission's standard contractual clauses and the provider's own adequacy arrangements. The specific instrument for each is a point docs/LEGAL.md flags for legal review.

How long it is kept

"Until we get around to it" is not an answer, so here are the actual periods. The rows marked as automatic are executed by a scheduled deletion job that runs against this same table of numbers; the rows marked manual say so, rather than pretending to a timer that does not exist.

WhatKept forWhat happensWhy that long
Viewing measurement (dwell, zooms, views, opens)
signal_events
2 years Deleted automatically
Deleted by created_at.
Two years is two full seasons of fairs and biennials, long enough for a gallery to compare this year’s room to last year’s, and the point past which attention data is about a person’s past, not their interest.
Marks (shortlisted works)
marks
2 years Deleted automatically
Deleted with the visit they belong to (ON DELETE CASCADE from room_sessions), and directly by created_at.
Same class of data as the measurement above; kept and dropped with it.
Visits
room_sessions
2 years Deleted automatically
Deleted by started_at, cascading to its events and marks.
A visit with no events left in it is a row about a person that answers no question.
Hashed IP address and browser user agent on a visit
room_sessions.ip_hash, room_sessions.ua
90 days Blanked automatically, the row kept
Set to NULL in place, on visits older than 90 days.
These two are security and abuse-detection data, not analytics. The unique-visitor count they support is a short-term question; after three months they are a fingerprint with no live purpose. Scrubbed to NULL while the visit itself stays, so the gallery’s signal history is untouched.
Dead viewing-room links
room_links.token
1 year Blanked automatically, the row kept
Token replaced with a non-resolving tombstone, one year after it expired or was revoked.
A token is a live credential. One that has been expired or revoked for a year has no purpose, and a stored credential with no purpose is only a risk. The row stays so the gallery keeps the record of who was invited to what.
Sign-in sessions
sessions
30 days Deleted automatically
Rows past expires_at are deleted.
The cookie lifetime. An expired row authorises nothing and only records that a device once existed.
Password reset tokens
password_reset_tokens
7 days Deleted automatically
Rows more than 7 days past expiry are deleted. Also swept on the write path by the reset endpoint itself.
A spent token is kept for a week so that "I did not ask for this" can still be answered, then it goes.
Push notification devices
push_devices
90 days Deleted automatically
Revoked rows deleted after 90 days; live rows not seen for 365 days deleted.
A device that unsubscribed is kept for 90 days so it is not accidentally re-subscribed, then deleted. A device not seen for a year has been wiped or reinstalled and its token is dead anyway.
Data-rights requests
data_requests
3 years Deleted automatically
Completed or rejected rows deleted after 3 years. Unverified rows are dropped after 7 days, because an unverified request is not a request.
Three years, to be able to prove a request was answered and when. This is the record an erasure request deliberately does not erase, Art. 5(2) accountability would otherwise be unprovable.
Collector contact records
recipients, contacts
No fixed period Nothing automatic. See why. These belong to the gallery’s own client relationship and the gallery decides how long that lasts. Nothing automatic deletes them. They go when the person asks (see below), or when the gallery deletes the contact, or when the gallery’s account is closed.
Inquiries, threads and messages
inquiries, threads, messages
No fixed period Nothing automatic. See why. A sales enquiry is the gallery’s business record and may be subject to its own accounting retention. Nothing automatic deletes it. An erasure request de-identifies who it was from rather than destroying the gallery’s history of the sale, see /legal/privacy, "what erasure actually does".
Subscription and billing records
subscriptions, stripe_events
No fixed period Nothing automatic. See why. Kept while the gallery’s account is open. The invoices themselves are held by Stripe under its own retention, and Romanian accounting law fixes how long the issuing company must keep its own copies. That period is an accountant’s answer, not an engineer’s, and it is listed in docs/LEGAL.md rather than guessed at here.
Follow-up drafts and what was sent to the model
nothing is stored
No fixed period Nothing automatic. See why. There is nothing to keep. The prompt is assembled per request and the draft is returned to the screen; neither is written to the database. What the provider itself retains is set by the account’s terms with them, which is a contract to check rather than a column to prune.
Artwork images in object storage
Cloudflare R2
No fixed period Nothing automatic. See why. Gallery inventory, not personal data. Deleted when the work or the gallery is deleted.
Database backups
[[BACKUP_RETENTION]]
No fixed period Nothing automatic. See why. An erasure removes the row from the live database immediately; it leaves backups when they expire on their own schedule, and backups are never used to answer a query. The retention of that schedule has not been fixed yet, it is listed in docs/LEGAL.md as a value to fill in.

Your rights, and what happens when you use them

You can ask for a copy of everything held about you (Article 15 and Article 20), have it corrected (Article 16), have it erased (Article 17), have its use restricted (Article 18), or object to it (Article 21). Where the basis is your consent, you can withdraw it at any time, and withdrawing does not undo the past but does stop the future.

The form is here. It needs no account. We answer within one month, as Article 12(3) requires.

Why you will be asked to click a link in an email

If you have no account, the only thing identifying you is an email address, and an email address is not proof of anything. So a request made with one does nothing at all until you use the link we send to that address. The link works once, expires in 24 hours, and asking again cancels the previous one. Without that step, anybody who knew your address could read your viewing history, or erase it.

What erasure actually does

Being specific here matters more than sounding reassuring. When you ask us to erase your record:

Backups are a separate matter and we will not pretend otherwise: an erasure takes effect in the live database at once, and a copy may remain inside an encrypted backup until that backup expires on its own rotation. Backups are only ever used to restore from a failure, never to answer a query, and a restored row is erased again.

Security

Contact, and complaints

Write to stoiana00@gmail.com, or use the form. We have not appointed a Data Protection Officer. Whether this processing requires one under Article 37(1) is one of the questions docs/LEGAL.md puts to a lawyer; if the answer is yes, this line will be replaced with their details.

You can also complain to the Romanian supervisory authority without going through us first: Autoritatea Nationala de Supraveghere a Prelucrarii Datelor cu Caracter Personal (ANSPDCP), https://www.dataprotection.ro, anspdcp@dataprotection.ro, B-dul General Gheorghe Magheru 28-30, Sector 1, 010336 Bucharest, Romania.

Changes

This notice carries a version, currently 2026-09-11.1, and the version is recorded against every consent given while it was in force. When the version moves, consents recorded under the old one are consents to different words, and the stamp is what lets us tell the two apart instead of guessing. Re-asking a collector whose consent predates a material change is a screen that has not been built yet; it is listed as owed work in docs/LEGAL.md, and this sentence will say something stronger when it exists rather than before.