Privacy policy

Last updated 2026-08-25.

DomainCanary stores what it needs to read your DMARC reports, and this policy says what that is.

Who we are

DomainCanary is a DMARC monitoring service at domaincanary.com. It is operated by Final Dynamics LLC, doing business as DomainCanary.

The contact for this policy is contact@domaincanary.com.

What the service is

Customers publish a DNS record naming an address we control. Mailbox providers (Google, Microsoft, Yahoo and others) then send us aggregate reports about mail claiming to be from that customer's domain. We parse those reports and email the customer a weekly digest.

The free plan covers one domain. The paid plans add domains and features: realtime alerts, one consolidated digest instead of one per domain, the option to skip the digest on a week where nothing needs attention, and the DNS cleanup views in the dashboard. They are billed monthly through Stripe: card details are entered on Stripe's own pages and held by Stripe, never by us. Stripe is the seller of record for the payment, under its Link brand, which is why receipts arrive from Link and why Stripe collects a billing address.

Eight free tools run on the public site and need no account: checkers for DMARC, SPF and DKIM, one checker that reads all three at once, a generator for an SPF record and one for a DMARC record, a pre-flight that compares a proposed SPF record against the published one, and an analyzer that reads a DMARC aggregate report you paste or upload. None of them stores what you submit; what each one does with it is under The free tools.

What we store

Account

When you create an account we store:

Subscription and payment

Buying a plan happens on Stripe's pages. No card number, expiry date or security code reaches this product, and none is stored here.

Opening a checkout is the moment anything of yours first goes to Stripe. We send them your account's email address, the id of the plan's price, and our own numeric user id, which comes back with the completed session so we know which account paid.

Stripe then collects two things from you that we never ask for and never receive: a name and a billing address. It needs them to work out the sales tax, VAT or GST it charges and pays to the tax authority. They are held by Stripe, they sit on the customer record in our Stripe account, and this product does not copy them into its own database or show them anywhere in the dashboard. Stripe may also offer to save your details as a Link account, which is an account with Stripe rather than with us.

What we store is what Stripe tells us: the Stripe customer id on the account row above, and one row per account holding the Stripe subscription id, the price id, the subscription status (active, trialing, past_due, canceled and the rest), when the current paid period ends, when we last wrote the row, and the timestamp of the newest Stripe event we applied, which is what a later event about the same subscription is checked against so a retried delivery cannot undo a newer one.

The card, the invoices and the payment history sit with Stripe. What Stripe receives is under Who else holds the data.

Sign in with Google

Signing in with Google is optional, and the buttons exist only where a Google client id and secret are configured.

If you use it, Google returns an id token to us over our own connection to Google. We store two things from it: Google's subject identifier for that Google account, which is the stable id a later sign-in is matched on, and the email address the token asserted, as it stood when the link was made. The row also carries the date it was created.

We check the token and do not keep it. Neither the id token nor the access token from that exchange is stored, and neither is written to a log.

Linking a Google account to a DomainCanary account that already exists sends a notice to the account address, because a link nobody was told about would be a silent takeover. The one address it is kept from is one that has already told us to stop mailing it.

A Google sign-in in progress uses a second cookie, described under Cookies.

Session

We store a SHA-256 hash of the session token, never the token itself, plus created_at and expires_at.

The cookie is dc_session. It is host-only and HttpOnly, with SameSite=Lax, path /, a fixed 30 day Max-Age, and Secure when the site is served over HTTPS. It is a login cookie. DomainCanary sets no advertising, analytics, or tracking cookies.

We set it after a successful sign-in, after you verify your address, and after you reset your password. Expired session rows are swept from the database on a later request, at most once every ten minutes.

Single-use tokens

We store SHA-256 hashes of email-verification and password-reset tokens, with an expiry and a used_at. Verification links are valid for 24 hours. Password reset links are valid for 1 hour.

A spent link is deleted, not just marked used. A cleanup pass runs every 15 minutes and removes every token row that has been used or has expired. People often assume a used password-reset link lingers in a database somewhere. Here it does not.

Unsubscribe links have their own records, so a mailbox can stop our mail without signing in. Each record holds a token hash, the email type the link stops, the domain or account it belongs to, the recipient email address, the date it was created, and the date it was used. Issuing a later token for the same domain or account clears records older than 400 days. Without a later token they stay.

Domains

For each domain on an account we store:

Aggregate reports

Per report we store the reporting organisation's name, the report id, the period, the DMARC policy observed, and the time the report reached us.

Per record inside a report we store a sending IP address, a message count, the disposition, DKIM and SPF results, the header-from value, the envelope-from value, the DKIM and SPF signing domains, and up to 10 DKIM authentication results with their selectors.

We store the header-from and envelope-from values as the report supplied them, up to a length limit. The envelope-from value can be a full mailbox address rather than a domain on its own. We do not trim it to a domain.

The raw report email

Mailbox providers send reports as email. Cloudflare Email Routing and our Email Worker receive the whole message and forward it to the origin, which holds the message and its attachments in memory while it parses them.

Two stores receive it from there. The archive bucket listed under Who else holds the data keeps the whole message, byte for byte, filed by domain and by the day it arrived, for 12 months. A report is sent once and cannot be asked for again, so the archive is what lets us read back what a provider actually sent: after a parser fix, or when a customer asks where a number came from. A message our parser rejects is archived all the same, and a report file uploaded by hand in the dashboard is archived the same way. The database keeps the parsed fields above, plus a count of how many bytes the message was; the raw message, its headers, and its attachments stay out of it.

When a message is rejected, the reason can name an attachment filename, and the forwarder logs the reply.

Sending IP addresses in those reports

A DMARC aggregate report names the IP addresses that sent, or spoofed, mail as the customer's domain. Those IPs belong to whoever was on the sending side of that mail. That can be the customer's own servers, a provider the customer sends through, or someone forging the domain. The report does not say which.

Mailbox providers generate these reports and send them to the address in the customer's DNS. The data is unsolicited: we do not choose its contents, and we cannot obtain consent from the operator of each IP it names. That is how DMARC reporting works.

An IP address can be personal data under the GDPR. The IPs in a report can therefore be personal data about people who have no account with us and no contract with us.

We store those IPs as they arrive, parse them, and show them to the account that owns the domain they were reported against. We do not pool report data across customers. We do not sell it, rent it, or publish it.

If you run a domain with us, you are the person who published the record that sends this data to us. Whether you owe notices of your own to the people behind those IPs, or to your own senders, depends on the law that applies to you; we do not send notices on your behalf.

Your own IP address, when you use the site

We use the visitor IP address to throttle abuse on the free tools and on the signup, login, resend, and password-reset endpoints.

The key sits in the running process's memory, with a 10 minute window for the tools and a 15 minute window for the auth endpoints. Old entries are dropped the next time the limiter runs, and everything in it disappears when the process restarts.

Your IP also reaches the origin access log, described under Logs.

Creating an account through the email and password form writes that IP address to the account row as well, together with the browser user-agent string the request carried. Signing up with Google writes neither. Both sit on the row until they are cleared 90 days after the account was created. What they are for, and how they are stored, is under Account.

A signup submission that trips one of the bot checks on that form writes the IP address and the user-agent to a record of the trip as well. That record exists whether or not an account was created, and it is described under Anti-abuse ledgers.

Derived and operational records

From the reports and from running the service we also store:

Addresses that refused our email

Two tables record a refusal.

The suppression table stores a canonical mailbox key, a reason (bounce, complaint, or declined), and a date. An unsubscribe is not a suppression: it turns the matching notification setting off, and the settings pages turn it back on.

The digest-decline table stores a hash of the declined confirmation token, the email address, and a date. It carries no reason. It exists so that pressing the same decline link twice gives the same answer.

Both are tied to no account. See What survives deletion.

Anti-abuse ledgers

Two ledgers stop one mailbox being flooded through signup or confirmation.

The auth-mail send ledger records a mailbox key, the kind of message, and a timestamp. The digest-confirmation send ledger records a mailbox key and a timestamp, with no message kind.

The same cleanup pass that clears spent tokens runs every 15 minutes and deletes auth rows older than 14 days and confirmation rows older than 7 days. Those two figures are enforced maximums, not intentions.

The signup form carries two checks of its own, and a third field that only watches. One check is a box hidden from anyone filling the form in a browser, which a person therefore never ticks; an unticked box sends no value at all, so arriving ticked is the whole signal. The other is a hidden stamp of the moment the form was drawn: a submission arriving less than two seconds after that was not typed by a person. A submission that ticks the hidden box, or whose stamp is too recent, missing, unreadable, or in the future, is discarded. No account is created and no email is sent.

A discarded submission is answered with the same page a real signup gets, so whatever sent it cannot tell the two apart. An error message would say precisely what to change.

The third field is a hidden text box that stops nothing. When it arrives filled the signup proceeds as normal, account and confirmation email and all. It is there to find out whether a field of that kind would ever catch a real person, before anything is allowed to depend on one.

All three write a line to the application log naming which of them fired and the IP address the submission came from. No email address is in that line. Those lines sit with the rest of the application log, under Logs, and last as long as it does.

All three also write a record to the database. It holds which check fired, whether the submission was blocked or allowed through, the address it was submitted with, the IP address and the browser user-agent the request carried, the source tag from the link the person arrived on, and the time. The two armed checks mark a record blocked; the field that only watches marks it allowed, because that signup went through. Unlike the log line, the record holds the email address. It is the record of a tripped signup check listed under Derived and operational records, and it is deleted 90 days after it is written, or sooner if the address it names has an account and that account is deleted.

The rows are kept so a rejection can be read back. The same answer that stops a bot learning it was caught stops a person learning it: somebody a check fired on by mistake waits for a confirmation email that was never sent, and the form gives them nothing to say so. The row holds the address they typed, which is what makes writing to them possible. Without it, a rejection that caught a real signup would leave nothing anybody could act on.

Retired waitlist

The retired early-access waitlist keeps the email address submitted, the domain submitted, the date, and the source tag from the link the person arrived on. Nothing writes new rows to it.

Logs

The origin writes an access log. It records the client IP address, request metadata, and the URL.

Single-use credentials are stripped before the line is written: verification, reset, digest confirm and decline tokens in token=, the authorization code and state value on the Google sign-in callback, and the one-click unsubscribe token in the URL path, are all replaced with REDACTED. Other things people put in a URL do reach the log, including the domain and selector typed into the DNS checkers. Email capture forms submit by POST, so the address is not put in the URL.

The access log rolls at 20 MB and five rolled files are kept. That is a size limit rather than a time limit, so the number of days it covers moves with traffic.

The application also logs to the container's output. Those lines can contain a recipient email address, a monitored domain, a reporting organisation and report id, header-from domains from a report, ingest dates and volume totals, and error detail. No rotation or retention period is configured for those logs today.

DNS lookups we make

The checkers, the two record generators, the SPF pre-flight, domain verification, the daily watch on your DMARC record, and the DNS cleanup views in the dashboard all send the queried name to a recursive DNS resolver. The checkers and the signing keys page also send a DKIM selector.

Once a day we also read each verified domain's own SPF record and the DKIM keys for the selectors its reports name, on every plan, and store the value when it differs from the last one we read. That is at most 21 queries per domain per day: one for the SPF record, and one each for up to 20 selectors. Nothing is guessed here: a selector is queried only when this domain's own reports name it, unlike the signing keys page, which also tries a short list of common names.

Anything that walks an SPF record queries the domains that record includes, which belong to the senders it names rather than to you. The signing keys page follows a selector's CNAME to wherever it points, which for a delegated selector is a sending provider's zone.

The free tools

The four checkers take the domain, and the DKIM checker its selector, in the URL. That is what puts them in the access log, as described under Logs.

The two record generators, the SPF pre-flight and the report analyzer are POST forms. What you type or upload is in the request body, which no log line touches, and every answer is sent with Cache-Control: no-store.

The analyzer is the one that receives a file. A pasted or uploaded aggregate report is parsed in memory and dropped when the response is written: no database row, no file on disk, no copy in a cache. That report is somebody's mail data, and it names third-party sending IPs the same way the reports we receive by email do. The limits are 5 MB on the submission, 8 MB decompressed, and 10,000 records; a report that breaks a limit or fails to parse is refused and the page says why. If a parse fails in a way the code did not expect, the error log gets the name of the exception and nothing out of the file. The name of the file you uploaded is echoed back to you on the page, shortened, and is not logged.

None of the free tools writes to the database. Their only ledger is the per-IP throttle counter in memory described above, which all eight share.

What this list does not cover

A domain typed into the four free checkers reaches the access log through the URL, as described above. Nothing else stores it, and what goes into the other four tools does not reach the log at all.

Support email you send to contact@domaincanary.com sits in Google Workspace, and is kept like ordinary business correspondence.

What we do not do

Why we hold it

We hold account, session, token, and domain data so you can sign in, prove control of a domain, and receive the digest.

We hold reports, the records inside them, per-domain sender records, daily ingest totals, the DMARC watch state, and sent-digest records so we can parse what mailbox providers sent and mail you the weekly digest.

We read that same report data to answer the questions the dashboard asks of it. The DNS cleanup views are the clearest case: they line up what your reports say about a sender or a selector with what your DNS says about it today, so a verdict is per mechanism and per selector rather than one number for the domain. The reports read are your own domains' reports. One thing is written down: DNS cleanup page snapshots keep the summary each of those pages last computed, so the page and the digest can say afterwards what they saw without working it out again.

We hold the trial stamps and the four product-notification stamps so the single trial an account gets is counted once, and so each email about it is sent once.

We hold the log of alert emails sent so the dashboard can show you which alerts went out and when. The alert records themselves are working state and are reused when a sender breaks a second time, so they cannot be the record of an email you already had.

We hold DNS record change history so a page can answer when one of your records changed. DNS keeps no history of its own: a zone file has no past and a resolver's memory is measured in hours, so the trail has to be collected before anybody needs it.

We hold the subscription state so an account gets the plan it is paying for, and a linked Google identity so a returning sign-in reaches the account it belongs to rather than a second one.

We hold the visitor IP throttle keys and the anti-abuse ledgers so signup and confirmation cannot be used to flood a mailbox, and so the free tools cannot be hammered.

We hold the record of a tripped signup check so a rejection can be looked at afterwards, and the person written to when the check turns out to have caught a real signup.

We hold the signup IP address and user-agent so a run of automated signups can be recognised as one run. An account created by a script looks like any other account on its own; those two fields are what makes two of them match.

We hold suppressions and digest declines so we do not write to an address that already refused. The reasoning for those two is in What survives deletion.

Who decides

We decide what the service stores, how long it keeps it, and how it is secured. Customers do not instruct us on any of that. Mailbox providers send the reports straight to us, at an address we control, because the customer published that address in their own DNS. We never receive the data from the customer.

Automated decisions

Nothing here scores you, ranks you, or profiles you. The digest reports what mailbox providers sent.

One automated control runs on ingest: a per-domain daily circuit breaker that stops processing a domain's forwarded report mail once the day's volume passes its budget. It acts on a domain's report volume. It reaches no conclusion about a person.

How long we keep it

The published retention floor, which this policy does not undercut: at least 12 months of report-level detail, source by source, plus daily totals per sending source for the life of the account, on every plan including free.

Today the code goes further than the floor. Nothing deletes reports or report records by age. They stay until the domain or the account is deleted.

Rendered digest HTML is kept for 56 days. The digest scheduler prunes it on each run, so a stopped scheduler delays the prune until it runs again.

A cleanup pass runs every 15 minutes and enforces six of these limits: expired sessions go, used and expired single-use tokens go, auth-mail send-ledger records go at 14 days, digest-confirmation send-ledger records go at 7 days, records of tripped signup checks go at 90 days, and the signup IP address and user-agent are emptied from the account 90 days after it was created. Those are maximums. The last of the six clears two fields rather than deleting a record, unlike the five before it: the account itself stays.

Records of tripped signup checks have a second limit the 90 days does not describe: at most 5,000 are held, and past that a new record pushes out the oldest. A burst of trips therefore shortens the life of what came before it, and the ceiling is there because the endpoint that writes these records is open to anyone.

The subscription state and any linked Google identity stay for the life of the account and go when it is deleted. So do the trial stamps and the four product-notification stamps: they are what stops a second trial and a repeated email.

DNS cleanup page snapshots are not deleted by age. There is at most one per domain per product, and the next render of that page replaces the one before it. They go when the domain or the account is deleted.

An alert row of any of the three kinds that has been mailed about is deleted 90 days after that mail, by the same 15 minute pass. A row still waiting to be mailed about is not deleted by age; it goes when the domain or the account does.

One sender-regression row outlives the 90 days on purpose: one that was mailed about and whose source has never been seen passing again. That row is the only record we hold that the outage was already reported, and deleting it would mail you a second time about a failure that never ended. It goes when the source recovers, 90 days later, or when the domain or the account is deleted.

A record in the log of alert emails sent is deleted 90 days after the mail it records, by that same 15 minute pass. Deleting one changes nothing about whether an alert is sent; the three kinds of alert record decide that on their own.

DNS record change history is not deleted by age. When a record last moved is the whole question the history answers, and a change from three years ago answers it. Each DNS record keeps at most 200 change events, and past that the oldest go first. They all go when the domain or the account is deleted.

Sessions last 30 days.

Verification tokens are valid for 24 hours, password reset tokens for 1 hour, and digest confirmation tokens for 7 days. The first two are deleted once spent or expired. A digest confirmation token lives in the domain row rather than the token table, so an expired one stays there until it is replaced, cleared, confirmed, or the domain is deleted.

When you delete the account, the rows listed under Deletion are erased in the same transaction. The exceptions are under What survives deletion.

Email we send

Transactional mail: address verification, password reset, a notice when someone tries to sign up with an address that already has an account, digest-address confirmation requests, and a notice to the account address when a Google account is linked to it.

An address other than the account's own receives a confirmation request. It must confirm before it receives a digest. The person at that address can refuse without having an account.

A digest is eligible only when the domain is verified, the weekly digest is on for it, a recipient resolves (the domain's own digest address, else the account's), that mailbox has confirmed, and that mailbox is not suppressed. A suppression comes from a permanent bounce, a spam complaint, or a decline. The weekly schedule and a duplicate-send guard also have to allow the run.

On a paid plan a domain also gets realtime alerts between the weekly digests, sent to that same confirmed digest address. The account settings turn them off everywhere, and each domain's settings can override that either way. Two things trigger one: a source nobody has seen before showing up failing DMARC, and a source that had been passing for that domain starting to fail. An alert is eligible on the same terms as the digest, and a domain sends at most one a day whichever kind it is.

Every digest, every alert, and both product-update notes carry List-Unsubscribe and the RFC 8058 one-click POST header, plus a visible unsubscribe link. Unsubscribing turns the matching notification setting off in our own database.

Your rights

What you can do yourself, right now

What to ask us for

Email contact@domaincanary.com. We act on a request that comes from the account's own address, and we ask for proof if it comes from anywhere else.

Ask for a copy of what we hold, a correction, deletion, a restriction on what we do with your data, or an objection to a particular use of it.

The product has no self-serve way to change the account email address. Email us if you need it changed, and expect us to check the request is real before acting on it.

If a report names your IP and you are not a customer

DMARC reports name sending infrastructure, so an IP you operate can appear in a report we hold for someone else's domain.

Against that IP we hold what the report said: the IP, a message count, a disposition, DKIM and SPF results, the header-from and envelope-from values, the signing domains, and DKIM selectors. We hold no name, no account, and nothing that tells us who runs the IP. We cannot look you up from it, and we cannot confirm an IP is yours.

Email contact@domaincanary.com and we will tell you what a report contains for an IP you can prove you operate.

Complaining about us

Email contact@domaincanary.com first and we will answer.

You can also complain to a data protection regulator instead of, or as well as, writing to us.

Children

DomainCanary is for people who run a mail domain. We do not ask anyone's age and we hold no age data. We do not knowingly create accounts for children. If you tell us an account belongs to a child, we delete it.

If there is a breach

If we find that account data or report data has been exposed, we email the address on the account. That mail says what happened, what we know so far, and what to do about it. We send it whether or not a law requires it.

Nobody outside the parties listed under Who else holds the data gets customer data from us. A binding legal order is the only thing that would change that.

Export

/dashboard/account/export returns a JSON download.

It includes the account profile with its digest address, that address's confirmation state, the seven notification answers the account gave, and the signup IP address and user-agent for as long as the account still carries them; each domain's name, dates, the local part of its reporting address, the token published in DNS to prove control, its digest address and confirmation state; reports and their records; per-domain sender records with first-seen and last-seen; and digest periods and send dates.

It leaves out the password hash, session tokens, single-use token records, the operator flag, the per-domain secret used internally, the legacy identifier stored for every domain, the rendered digest HTML, the archived original report files, the daily ingest totals, the DMARC watch state, pending confirmation fields, the per-domain notification overrides, the passing and failing counters on the sender records, unsubscribe link records, suppression and decline records, both abuse ledgers, the records of tripped signup checks, retired waitlist records, the plan, the Stripe customer reference, the trial stamps and the four product-notification stamps on the account, the subscription record, all three kinds of alert record (new-sender, failing-sender and sender-regression), the log of the alert emails those records produced, the DNS record change history, the DNS cleanup page snapshots, and the linked Google account records.

Four of those omissions have a reason worth stating. A linked Google account record holds an opaque subject id Google issued to us rather than anything the customer wrote, and the address beside it is the account's own, which the file already carries. An alert record of any kind adds nothing the file does not already hold: its source IP, message count and first-seen date are the sources and report records themselves, and the dates left over are our own record of writing the alert record, mailing about it, and seeing a failing source pass again. The DNS record change history holds no content of yours that the file does not already imply: the values are your own published DNS records, and what we add is the dates we read them on, which are our observations rather than your data. A DNS cleanup page snapshot is our own reading of those same records against your reports, worked out again from scratch whenever the page is opened, so it holds nothing you gave us.

The file carries every domain, report, record, source row and digest period the account holds, whatever the count. It is streamed as it is built, so its size is not bounded by what fits in memory. Until 2026-08-28 it stopped at 20 domains and 500 reports per domain, which meant an account on a plan allowing more domains than that could not export all of its own data. A category left out altogether has nothing to report, so the file cannot flag it. The list above is the full account of what is missing.

Deletion

/dashboard/account/delete requires the account's own address typed back and the password. An account created by signing in with Google has to set a password through the reset flow first, because the form asks for one.

If the account holds a running subscription, we cancel it at Stripe before anything is deleted, and if that cancellation fails nothing is deleted at all. A subscription Stripe no longer holds is the one exception: there is nothing left to cancel, so the deletion goes ahead rather than leaving an account that can never be erased. It then deletes, in one transaction:

the account, its sessions, its single-use tokens, its domains, reports, records inside those reports, DKIM results inside those records, sent-digest records, per-domain sender records, failing-sender alert records, new-sender alert records, sender-regression alert records, the log of alert emails sent, DNS record change history, DNS cleanup page snapshots, unsubscribe link records, daily ingest totals, the DMARC watch state, the subscription record, linked Google account records, the auth-mail and digest-confirmation send-ledger records filed under the account's own mailbox, and any retired waitlist record or record of a tripped signup check whose address resolves to that mailbox.

Two things about that are worth stating plainly.

It removes rows from the live database. It does not reach into the origin access log, the application logs, mail held by our sending provider, the raw report archive, or the machine snapshots and off-site backups described below. Those copies age out on their own schedules. The database is not byte-wiped either: the application runs no secure-delete or VACUUM pass after the transaction. Deletion does reach Stripe, but only to cancel: the customer, the cancelled subscription and the invoices stay as Stripe's own record of payments that happened, kept on Stripe's terms rather than ours.

What survives deletion

Deleting the account puts your data into three groups.

Almost everything goes at once. Every row listed under Deletion is gone when the transaction commits.

Two refusal records are permanent. The suppression list and the digest-decline list are not erased. A suppression row is a mailbox key, a reason (bounce, complaint, or declined), and a date. A decline row is a token hash, an email address, and a date. Neither is tied to an account.

They survive because a deleted-then-recreated account must not be able to email an address that already refused. That protects the person who asked not to be contacted. It is also a hard requirement of bulk sender rules. This is a legitimate-interest retention of a minimal record for the protection of the person who asked not to be contacted. If the departing user's own address is in those tables, it stays.

Confirmation-ledger rows about other people's mailboxes stay for up to seven days. If a domain on the account sent a confirmation request to an address that was not the account holder's, the ledger row for that send is filed under the recipient's mailbox, not under the account. Deletion clears the rows filed under the account holder's own address and leaves those. The 7 day cleanup pass removes them.

That is deliberate. The row exists so that one mailbox cannot be mail-bombed through the confirmation flow, and it counts against a daily limit protecting the person who received the mail. If deleting an account wiped it, anyone could clear a victim's limit by deleting and recreating an account, which turns an erasure feature into a way to attack a third party. The row is also not really the departing account holder's data: it is a record about somebody else, kept to protect them, and it expires on its own within a week.

Who else holds the data

These parties run the infrastructure.

Party Role What it receives Location
DigitalOcean The virtual server that runs everything The live database, the access log, the application logs, and whole-machine snapshots nyc3, United States
Cloudflare Authoritative DNS, reverse proxy in front of the site, and Email Routing that receives the inbound DMARC reports Visitor IP addresses and request metadata, and the complete inbound report email Edge, location not established by our own records
Amazon Web Services (SES) Sending outbound email The recipient address and the full message us-east-2, United States
Cloudflare R2 Off-site database backups and the raw report archive A copy of the database, and every report email and uploaded report file as it arrived Eastern North America (ENAM)
Stripe Seller of record and payment processing for the paid plans Your email address, our own user id and the plan's price id, the card details you enter with them, the name and billing address it collects to work out tax, and the subscription record Stripe, Inc., United States
Google Sign in with Google, for accounts that use it The sign-in request, which tells Google that one of its accounts signed in to DomainCanary Data region not established by our own records
Google Workspace The mailbox that receives support email sent to us Support correspondence Data region not established by our own records

Stripe's own privacy policy is at stripe.com/privacy. An account that never opens a checkout sends Stripe nothing, and nothing goes to Google for sign-in unless somebody uses that button.

The origin database is on the DigitalOcean server in nyc3, the SES region is us-east-2, and the backup bucket is in Cloudflare R2's Eastern North America region. Our own records do not establish where Cloudflare processes traffic, where Stripe processes payments, or where Google holds mail and sign-in data, so this policy does not claim a processing region for any of them. Customers outside the United States are sending their data to an origin in the United States.

If the business changes hands

If the service is sold or transferred, the data described here goes with it, and whoever takes it over is bound by this policy until they publish a replacement.

You get notice before that happens, at the account address, with time to export and delete first.

Security

No SOC 2. No ISO 27001. No penetration-test report. No cyber insurance we know of.

Cookies

DomainCanary sets three cookies. Two of them exist only to sign you in, and the third remembers how you asked the pages to look.

dc_session is described under Session.

dc_oauth is set only when you start a sign-in with Google. It carries the OAuth state value, the PKCE verifier, and the page tag the sign-in started from, between the redirect out to Google and the callback back. It is HttpOnly with SameSite=Lax and path /, expires after 10 minutes, is Secure when the site is served over HTTPS, and is deleted when the callback runs. A callback the rate limiter refuses leaves it to expire on its own. It holds no email address.

dc_theme is set when you pick light or dark in your account settings. It holds that one word and lasts a year. It is SameSite=Lax with path /, and Secure when the site is served over HTTPS. It is not HttpOnly, because the value carries no authority: it decides which colours a page is drawn in and nothing else. Picking auto again deletes it, and with no cookie the pages follow the setting on your device. Nothing about it is stored in the database, and it is not read by anything but the code that renders the page.

None of the three is an advertising, analytics or tracking cookie.

Changes

If this policy changes, the new version carries a new "last updated" date.

A change that reduces what we promise here follows the notice rule in the terms of service: existing users hear about it before it takes effect.

Contact

contact@domaincanary.com

Final Dynamics LLC, doing business as DomainCanary.