Privacy policy
What personal data CargoSharks holds, where it is hosted, who else touches it, and what the audit trail records. Written from the source code rather than from a template.
Terms of serviceData-processing addendumHow the enforcement works
Read this before you read anything else
This notice was assembled from the CargoSharks codebase — the database schema, the migrations, the integration adapters and the deployment documentation — by reading what they do and writing it down. That makes it unusually specific about mechanism.
Two rules were followed while writing it, and both still hold:
- No certification is claimed. CargoSharks holds no SOC 2 report, no ISO 27001 certificate and no third-party audit attestation, and does not describe itself as “GDPR compliant”. What is described below is what the software does. Whether that satisfies any particular law is a question for counsel and for the customer’s own assessment.
- Where the code and a conventional clause disagreed, the code won — and the disagreement was written down rather than smoothed over. Section 09 is the clearest example: records are corrected in place rather than erased, because the data is yours and the history behind a figure is part of what you are keeping.
01What this covers
This policy covers two quite different things, and confusing them is the usual way a software privacy policy becomes untrue.
- This website —
cargosharks.com, the seven static pages you are reading. Sections 03 and 04 are about this and only this. - The CargoSharks software, running in a deployment operated for a customer. Sections 05 onwards are about that. If you are an employee of a forwarder that uses CargoSharks, or a customer or agent of one, this is the part that concerns you — but your first point of contact is the forwarder, not us. Section 02 explains why.
It does not cover the demo deployment, which runs on invented data and holds no real record of anybody. It runs on fixtures with no database behind it, so nothing done there is recorded in any customer deployment.
02Who is responsible for what
CargoSharks is supplied as one deployment per customer: the forwarder gets their own database, in their own instance, with their own configuration. There is no shared table with a customer column in it, and no identifier above the branch anywhere in the schema.
That shape decides the roles.
- For this website and for enquiries
- CargoSharks Freight Technologies decides what is collected and why. In data-protection terms it is the controller.
- For everything inside a customer’s deployment
- The forwarder is the controller. They decide whose shipments are recorded, which mailboxes are connected, whether the model surface is switched on at all, and who may see what. We act on their instructions. The terms of that relationship are in the data-processing addendum.
- If you are a shipper, consignee or contact
- Your data is in a forwarder’s deployment because you did business with the forwarder. Ask them first. We will help them answer you, and section 14 says what we can and cannot do.
Which data-protection law governs a given deployment depends on where it is hosted and where the customer is established. Each customer’s contract records the hosting region, and this notice is written to the standard of the most demanding regime that applies to it.
03This website
The site you are reading is static HTML. It has no backend, no database and no accounts. What follows is not a promise about a system that could change — it is a description of files.
- It sets no cookies. None: not a session cookie, not a preference cookie, not a consent cookie. There is nothing to consent to.
- It runs no analytics. There is no Google Analytics, no Plausible, no Matomo, no Meta pixel, no LinkedIn tag and no heatmap script. Its one script file,
assets/site.js, does scroll effects and one form handler, makes no network request of any kind, and stores nothing in the browser. - The pilot form does not post anywhere. It composes a
mailto:message in your own mail client with your answers already in it, and you press send. Nothing reaches a server unless you choose to send an email, and the address is printed on the page so you can send it yourself instead.
The one third party
The page loads its typefaces — Manrope, IBM Plex Mono and IBM Plex Sans Arabic — from Google Fonts (fonts.googleapis.com and fonts.gstatic.com). That request carries your IP address and your browser’s user-agent string to Google, on every page load, and it happens before you interact with anything. It is the only external host this site contacts.
Whoever hosts this site will also keep ordinary web-server logs — IP address, time, path requested, user-agent.
04What you send when you enquire
If you use the pilot form or write to [email protected], we receive what you typed: your name, company, email address, phone number if you gave one, how many branches you run, and what you use today. We use it to reply to you and to have the conversation you started.
We do not sell it, share it or add it to a list you did not ask to be on.
05What the product holds
Inside a deployment, CargoSharks holds the record of a forwarding business. Most of that is commercial rather than personal — lanes, containers, rates, invoices. The personal data is listed here in full, because a list that omits the awkward categories is worse than no list.
| Whose data | What is held | Why it is there |
|---|---|---|
| People who sign in | Name in English and Arabic, email address, phone, interface language, whether the account is active, and when it was last seen. Separately: a hashed password, session records carrying IP address and user-agent, and two-factor secrets where two-factor is enabled. | Signing in, and knowing which branches and permissions apply to the request. |
| Customer and agent contacts | Name in English and Arabic, email, phone, their role at the company, whether they are the primary contact, and which notifications they have opted out of. | Quotations, documents, portal access and notifications go to a named person, not to a company. |
| People at the point of delivery | The name of whoever received the goods, their identity-document number, an image of their handwritten signature, the GPS latitude, longitude and accuracy of the delivery, and any photographs taken — of the goods, of damage, or of the signed paper. | Proof of delivery. This is the most sensitive category in the product, and it is about people who never signed up for anything. |
| People who write to a connected mailbox | The message: subject, full body text, sender, all recipients and copies, timestamps, and every attachment — stored as a record on the job. | An enquiry emailed to a branch becomes a proposal a coordinator accepts or rejects; a partner’s bill becomes matched lines. Raw MIME is not retained; the flattened text is. |
| Named parties on shipping documents | Shipper, consignee and notify-party names and addresses, as they appear on the bill. | They are what a bill of lading is. |
| Anyone in a document a model reads | Where the assistant surface is switched on, every request sent to the model provider and every response received is stored verbatim in the deployment’s own ledger — including any document that was inlined into the request. See section 07. | After a prompt-injection incident the first question is what the model actually saw, and a system that cannot answer it cannot be investigated. |
Cookies the product sets
Four, none of them for tracking or advertising:
- The session cookie —
HttpOnly, andSecurein production. It is an opaque reference checked against the database on every request rather than a self-contained token, so revoking a session takes effect immediately rather than whenever it happens to expire. Sessions last twelve hours. - The language preference (
qabandi_locale) and the theme preference (qabandi_theme) — one year, readable by the page’s own script because the page has to render in the right direction before anything else happens. - A demo persona cookie, on the demo deployment only. It is signed, and it is a demo gate rather than a security boundary.
06Where it is hosted
Each customer’s deployment runs in a region chosen with them and recorded in their contract. The database is a PostgreSQL instance outside the application stack; documents and images are held in S3-compatible object storage.
07Who else processes it
Every external service below is off by default and is switched on by the customer, from their own settings screen, with their own credentials. That is not a courtesy — the software has no vendor account to fall back on. Where a service is not configured, the feature that needs it degrades and says so rather than silently doing nothing.
Categories, and what actually reaches each one:
| Category | What it is | What reaches it |
|---|---|---|
| Object storage | S3-compatible storage, provider-agnostic. Any of Cloudflare R2, MinIO, Backblaze or AWS S3 speaks the dialect the product signs for. | Every document byte. Generated PDFs (quotations, invoices, delivery orders, house bills of lading and air waybills, customs packs, manifests), inbound email attachments, uploaded scans, proof-of-delivery signature images and photographs, partner compliance documents, branch letterheads. |
| The model provider | OpenRouter (openrouter.ai), which is a router rather than a model vendor: it forwards the request to whichever model is configured and may serve a different one, which is why the deployment records the model actually served alongside the one requested. The default driver is none — nothing is sent anywhere until a customer supplies a key. | Three surfaces. Enquiry reading: the subject and body text of an inbound email, truncated. Supplier-invoice reading: the whole document, inlined into the request as data — so a commercial invoice or a customs document leaves the deployment in full. The assistant: the rows returned by its tool calls, which are exactly the rows the person asking is permitted to see. |
| Outbound mail | An SMTP relay the customer provisions. No provider is named or bundled. TLS is required by the adapter, and certificate verification is on by default. | Recipient addresses, message subjects and bodies in either language, and attachments — quotations, invoices, proof of delivery, password-reset mail. |
| Inbound mail | The customer’s own IMAP mailboxes, one per branch, polled by the service. Passwords are sealed before they are stored (section 12). | Nothing goes out. What comes in is the point: the product reads the connected mailboxes, including message bodies and attachments. |
| Carrier tracking | Maersk, Hapag-Lloyd and CMA CGM, over the DCSA standard, on the customer’s own carrier credentials. Built and tested; not running anywhere today, because no carrier keys are held. | Container numbers, bill-of-lading and booking references, carrier codes. No personal data, by design. |
| Exchange rates | A public rates endpoint (open.er-api.com), configurable to a commercial feed. | Currency codes. Nothing else. |
| Webhooks you configure | Signed outbound webhooks to URLs the customer chooses, retried and logged. | Whatever the customer subscribed to, sent wherever the customer pointed it. That is a transfer the customer directs, not one we make. |
Things people expect to find here and will not
- No payment processor. There is no Stripe, no KNET integration, no card handling of any kind anywhere in the product. Payments recorded in the finance module are records of money received elsewhere.
- No analytics or product telemetry by default. The application is instrumented against the OpenTelemetry interface, but ships with no exporter configured, so the instrumentation resolves to nothing. If a customer configures a collector, spans carry the deployment key, the acting person’s identifier and the branch.
- No error-reporting service. No Sentry, no Datadog, no Bugsnag. Errors are written to the container’s log as structured JSON.
- No e-invoicing provider. The hand-off is built; no accredited provider has been appointed, so nothing is transmitted.
- No WhatsApp. The webhook verifies signatures and nothing consumes it. Nothing is sent and nothing is read.
The notice period before a sub-processor is added or replaced, and the customer’s right to object, are set out in the data-processing addendum.
08What is recorded about every change
Every insert, update and delete on every business table writes a row to the deployment’s audit log. It is written by database triggers rather than by application code, so a change made by a script, or by someone with a database client, is recorded exactly as a change made through a screen would be.
Each audit row carries: when, who (the acting person’s identifier and what kind of principal they were — staff, customer contact, agent contact, support, or the system itself), which table and which row, what operation, the whole row before and the whole row after, the branch, and the IP address the request came from.
There is no request identifier and no user-agent string on an audit row. The user-agent is held on the session record instead.
Four kinds of table are excluded from this, on purpose: the audit log itself, the authentication tables (a log that copied credential rows would be a new place to steal them from), rate-limit counters, and the record of raw inbound webhooks.
UPDATE, DELETE and TRUNCATE on the audit log are revoked from the account the application runs as and from the public role, and only INSERT and SELECT are granted. So no action taken through the product — by your administrator, by our support, or by anyone holding an API key — can amend or remove an audit entry. The role that owns the table can still alter it. That role is the migration account; it is not used at runtime, it is not held by support, and migrations are applied by continuous integration from tagged commits. That is a procedural control on top of a structural one, and it is worth stating rather than rounding up to “impossible”.09Nothing is quietly deleted
This is the section a data-protection reviewer will look at most closely, so here is exactly what happens to a record, and why.
Records are never deleted in place. That is structural, not a policy: DELETE and TRUNCATE are revoked from the application’s database account on every table, and the default privileges granted to it on future tables are SELECT, INSERT and UPDATE only. A new table inherits the same inability. Records are instead superseded, closed or deactivated: a rate card is versioned, a user is deactivated, a quotation revision supersedes the one before it and the earlier one stays readable.
Files behave the same way. Retiring a document copies the object under a retired/ prefix and removes the live key; the bytes survive, and what happens to them afterwards is decided by a lifecycle rule on the bucket or by a person — not by the application.
A single record is not erased in place. Records are corrected by superseding them rather than by overwriting or deleting them, so a correction is honoured going forward while the history behind it stays intact. What ends the data altogether is deletion of the whole deployment at the end of the engagement, which is yours to make — your data is your own (section 14).
10How long things are kept
Four periods are actually implemented in the software and are quoted here exactly:
- Sign-in sessions
- Twelve hours, refreshed at most hourly.
- Language and theme cookies
- One year.
- Failed background jobs
- Thirty days. A notification that never arrived is an incident to look into, not a row to sweep up.
- Support access grants
- Two hours by default, eight at the most. See section 11.
Everything else is undecided, and in practice indefinite. The specification asks for audit entries and access logs to be kept for at least twelve months; because nothing can delete them, they are in fact kept forever. Application logs, web-server logs and object-storage lifecycle are all outside the software and are not defined anywhere.
Backups
Backup tooling exists and is used: a per-deployment dump taken through an administrative connection, which the tool refuses to take through the ordinary application account because row-level security would make such a dump silently partial. Restores are drilled into a scratch database and verified by row count and schema rather than by the exit status of the restore command — a dump of an empty database restores perfectly.
What that tooling deliberately does not do is retention, off-site copying or encryption at rest; those belong to the storage the dumps are written to. The specification asks for daily backups kept fourteen days and weekly off-site copies kept thirty, with quarterly restore drills evidenced in writing. That is a requirement, not something the code enforces.
One property is worth knowing: mailbox passwords and integration credentials are sealed with a key held in the environment and never in the database, so a backup or a replica carries ciphertext and no key.
11When our support can see your data
Not unless your administrator grants it, and then only within the boundary they set. The mechanism, exactly as implemented:
- Your administrator writes the grant, not us. A support principal cannot create, extend or renew its own access — it is refused in the application and it has no policy admitting it in the database.
- A reason is required. The database will not accept a grant with an empty one, and the form asks for the ticket or the request it is for.
- It is scoped to named branches. Leaving the selection blank does not mean everything; it defaults to the branches the granting administrator holds themselves.
- Read is the default and is structurally read-only. A support session has select policies and nothing else, so an attempted change does not fail a check — it finds no rule that admits it at all. Granting write requires typing the words
GRANT WRITE, because escalation should feel like a different act. - It expires on its own — two hours by default, eight at the most, one at the least. The expiry is checked by the database policy on every statement, not by a cleanup job that might not run.
- You can revoke it in one click, from the banner, and it takes effect on the next statement.
- A banner shows on every internal screen while a grant is live, naming the engineer, the scope, the stated reason, the branches and the expiry time.
- Every session opened under a grant is logged — one row per database transaction, written centrally so there is no path that forgets. The log is insert-only, and your administrator can see the access count against every grant ever issued without asking us for it.
12How stored credentials are protected
Credentials the product must be able to replay — a mailbox password it uses to log in, an API key it presents to a carrier — are encrypted with AES-256-GCM. A fresh 96-bit initialisation vector is generated per seal by the sealing code itself; no caller gets the chance to supply one. The stored form carries its scheme version, so the algorithm can be changed without guessing at what old rows are.
The key is 32 raw bytes supplied through the environment. A passphrase is refused rather than stretched into a key by an unspecified derivation. The key is never in the database, which is the property that makes the rest of it worth anything: the mailbox table is in every backup, every replica and every dump somebody takes to debug something, and none of those carries the key.
What is encrypted this way: mailbox passwords, and the secrets on integration settings — the model provider key, the SMTP password, the object-storage secret, carrier credentials, webhook signing secrets. A database constraint enforces the sealed shape on mailbox passwords, so a plaintext password cannot be written by a script or by hand.
Credentials that never need replaying are not encrypted at all — they are hashed. API keys are hashed with Argon2id; session tokens are opaque references looked up in the database.
13Security — and what is not in place yet
What the software does is described above and in the addendum: separation enforced by the database rather than by application code, an audit trail written by triggers, no delete permission anywhere, sealed credentials, scoped and expiring support access. Those are properties of the build and can be checked against it.
What is not in place is listed here rather than omitted, because a security section that only lists strengths tells a reviewer nothing:
- The database connection does not specify TLS. Traffic between the application containers and PostgreSQL runs however the connection string and the server negotiate; nothing in the code requires encryption on that hop. Traffic between the web and service containers is plain HTTP on a private network, deliberately.
- Encryption at rest for the database itself is not established anywhere. It is a property of the chosen host, and no host has been chosen.
- Audit search and export is not built. The log is complete, immutable and readable, but no screen searches or exports it.
- No in-place erasure of a single record. Records are corrected by superseding, not deleting — right for an audit trail. Deletion of the whole deployment at the end is yours to make. See section 09.
- Monitoring and alerting are not in place. Tracing is wired with no exporter registered.
- No independent audit has been performed. No penetration test, no SOC 2, no ISO 27001. The security properties described here are checked against a live database by the project’s own tests, which is verification, not attestation.
Two entries left this list on 25 August 2026, and are named here rather than quietly removed. Security headers are now set by the application — a content-security policy with a per-request nonce, HSTS, frame-options, referrer and permissions policies. Sign-in lockout is built: five failed attempts lock an account for fifteen minutes, alongside a per-address rate limit, and two-factor authentication is now enforced for the Administrator and Finance roles rather than merely offered.
14Your rights, and how to exercise them
Which rights you have depends on which law applies to you, which is set out in section 02. What we can do about each, mechanically, does not depend on that at all:
- Access — what is held about you
- Available. A deployment can be queried for every record naming a person, and the audit log can be read for every change touching them.
- Rectification — correcting something wrong
- Available, and it leaves a trace. Correcting a record supersedes it rather than overwriting it, so the earlier value stays in the audit log. That is the honest answer: the wrong value is corrected, not unwritten.
- Portability — getting the data out
- Available, with a caveat. A complete export in an open format is offered commercially and there is nothing structural in the way of it. What is not built is a single self-service action that produces it: the specification calls for a full export as one action and records it as not implemented. Today an export is assembled for you rather than pressed by you. There are also a financial-journal export and cutover import templates in the product.
- Erasure — being deleted
- A single record is not erased in place; it is corrected by superseding it (see Rectification, above, and section 09). Your data as a whole is yours to delete at the end of the engagement (section 14).
- Objection and restriction
- An account can be deactivated and a contact can be marked inactive or opted out of notifications, both immediately. Neither removes anything.
If your data is in a forwarder’s deployment, your request goes to the forwarder — they decide, and we assist them. If it concerns this website or an enquiry you sent us, write to us directly.
We answer a request as quickly as we can, and within the period the applicable law allows. We will ask you to confirm your identity before we act on one, because acting on an unverified request is itself a disclosure.
15Transfers out of the hosting region
The deployment stays in its region. Two things can leave it, and both are the customer’s own switch:
- Model requests, if the assistant surface is enabled. Those go to OpenRouter, which routes onward. Where the model is served from is not something the product controls or is told, and this is the single largest residency question in the system: a customs declaration or a commercial invoice inlined into a request body has left the region.
- Mail, to whatever relay the customer points it at, and webhooks, to whatever URL the customer configures.
A customer with data-residency obligations should leave this surface switched off, and we will say so in writing. It is off unless a key is configured, and removing the key removes it entirely — there is no partial state and no residue.
16Changes to this policy
We post any change to this notice on this page, with the date it takes effect. Where a change materially affects a customer’s deployment, we tell that deployment’s administrators before it takes effect.
17Contact
Write to [email protected]. A named person replies, not a ticket queue.
Write to [email protected] with any question about this notice or to exercise a right. To report a vulnerability, write to [email protected] — the terms are at /.well-known/security.txt.
If your data is in a forwarder’s deployment, ask the forwarder first. They are the controller and we act on their instructions.
The set