What goes to the server when you publish: data flow and privacy in a Markdown SaaS

If a product asks users to upload content, the question "what happens to my documents?" cannot be answered vaguely. The architecture must be explainable in a table, in a diagram and in a written promise.

Important

The privacy promise is not legal copy. It is a technical contract: what data enters, who processes it, how long it lives and how it is deleted.

The promise in plain language

Promise Technical implication
Content is used only for publishing It is not reused for training, text analytics or internal datasets
It is encrypted in transit and at rest TLS is mandatory and storage is encrypted
You can download it Full export in Markdown and original assets
You can delete it Physical deletion of content and backup deletion within a defined window
The server does not receive the whole workspace Payload is limited to the site selected for publishing
Note

If the product cannot technically sustain this table, the privacy policy should not promise it.

Publishing flow

sequenceDiagram participant U as User participant PS as Publishing service participant Auth as Identity service participant Lic as Licensing service participant RS as Render service participant Storage as Object storage participant Edge as Static hosting U->>PS: Publish site (token + selected content) PS->>Auth: Verify identity and scopes Auth-->>PS: User authorized PS->>Lic: Validate plan, quotas and features Lic-->>PS: Entitlement OK PS->>RS: Render Markdown to HTML RS-->>PS: Safe HTML PS->>PS: Apply theme and brand tokens PS->>Storage: Upload assets PS->>Edge: Upload static bundle Edge-->>PS: Deployment ID + URL PS->>PS: Record audit log PS-->>U: Published URL

What travels to the server

Data Why it travels Who uses it Retention
Site Markdown Convert pages to HTML Publishing and render services Versions according to plan
Site config Domain, navigation, locales and theme Publishing service While the site exists
Brand tokens Logo, colors, font and favicon Publishing service While the site exists
Referenced assets Public images and files for the site Object storage / static bundle While the site exists
Identity token Verify identity and scope Identity service Not stored as content
License context Validate plan and quotas Licensing service According to billing/audit policy

What does not travel

Outside the payload

  • Full local workspace.
  • Repos that are not part of the site.
  • Local edit history.
  • Local editor configuration.
  • Personal files outside the published repo.

Should never travel

  • Private keys.
  • Provider API keys.
  • Local Git credentials.
  • Personal tokens.
  • Secrets used by other projects.
Warning

"Publish a site" cannot mean "upload my machine". The contract must be explicit: only the selected site content is sent.

Service responsibility

flowchart TD user["User"] --> payload["Publishing payload"] payload --> publish["Publishing service<br/>orchestrates and audits"] publish --> auth["Identity service"] publish --> license["Licensing service"] publish --> render["Render service"] publish --> storage["Object storage<br/>assets and versions"] publish --> pages["Static hosting<br/>published site"]
Service Must be able to Must not do
Publishing service Orchestrate publish, apply theme, audit Train models with content
Identity service Verify identity and scopes Read Markdown
Licensing service Decide quotas and features Receive site content
Render service Convert Markdown to safe HTML Store content indefinitely
Object storage Store needed assets and versions Expose private assets without policy
Static hosting Serve the published bundle Know user secrets

Why not do everything locally

A local alternative makes sense, but it does not solve the full SaaS case.

Need Local SaaS
Export static ZIP Yes Yes
Publish with managed custom domain Manual Automated
Enforce plan and quotas Not authoritative Authoritative
Protect premium themes Hard Yes
Audit log and rollback Manual Centralized
Deploy without user tokens Not applicable Yes
Tip

The right private mode is export ZIP and deploy it yourself. The SaaS exists to automate deploys, domains, quotas, themes and rollback.

Deletion architecture

Deletion must be a flow, not a flag.

flowchart LR request["Deletion request"] --> auth["Verify owner"] auth --> live["Delete published site"] live --> store["Delete assets/versions in object storage"] store --> audit["Record deletion event"] audit --> backups["Propagate to backups<br/>maximum 30-day window"] backups --> done["Confirmation"]
Minimum operational privacy checklist
  • TLS required on every endpoint.
  • Encryption at rest for content and assets.
  • Versions retained according to plan, not indefinitely by default.
  • Physical deletion from object storage on deletion request.
  • Documented backup window.
  • Audit log for publishes, changes and deletions.
  • Human access to content only with explicit support authorization.
  • Written policy that matches the real architecture.

Risks and controls

Risk Control
Uploading more content than needed Explicit site selector and payload preview
Accidentally exposing secrets Exclusion list, validation and warnings before publishing
Bypassing quotas from the client Authoritative validation in licensing service
Premium theme leaking to Free client Server-side render and bundle per plan
Retaining deleted content Deletion flow with event and backup window
Support accessing content Temporary, authorized and audited access

What makes me trust the design

Minimization

Only what the site needs for publishing travels.

Separation

Identity and licensing do not read content; render does not decide quotas.

Reversibility

The user can download, export ZIP or delete the site.

The rule I keep

A SaaS that receives user content needs a written promise and an architecture that can sustain it. If one fails, the other cannot save trust.


This article is part of my notes on data and privacy in SaaS. If you are interested in this topic, follow the blog or contact me.