Skip to content

Security

This property holds no credential, no secret and no identity truth.

It is a static content site with an acquisition handoff. It stores nothing about you, authenticates nobody, and fetches nothing at render time — so a page here cannot fail because another system is unavailable, and cannot serve a stale copy of live state under our own name.

What is left worth describing is the header set this origin sends, the build that refuses to ship a link pointing somewhere unexpected, and an honest account of what none of that establishes.

Where things live

The interesting material is somewhere else, on purpose.

Keeping authentication, commerce and canonical truth off a public content property is the largest security decision on this page, and it was made by not building them here.
Each surface in the estate, what it holds and what it never holds
SurfaceWhat it holdsWhat it does not
agents.ecocitizenz.comStatic content, an acquisition handoff, and a client-side check of whether an identifier is well formed.No account, no session, no password, no API key, no payment detail, no identity evidence, and no copy of any record.
trustops.ecocitizenz.comAuthentication, the account, Passport issuance, capacity and commerce. Every credential-bearing interaction in this estate happens there.Nothing about it is reachable from this property. Following a call to action is a top-level navigation, not an embedded flow.
ECZ-ID CoreCanonical identity truth: what is actually true about an identity, and the evidence behind it. The Resolver publishes a projection of it.It is not addressable from this site. This property has no build-time or run-time link to it.
resolver.ecocitizenz.orgThe public record, read-only, unauthenticated and unmetered. One place where current state is true.No write path exists from here, and nothing on this site caches, mirrors or restates what it publishes.

The Resolver is read-only and public by design: reads are unauthenticated, unmetered and available to anyone, including people who have no relationship with us at all. That is a deliberate property of the system rather than an oversight in it.

Response headers

What this origin sends, and why each one is what it is.

They are set in source and applied at the origin rather than only at an edge, because the origin is reachable on its own platform hostname — headers configured only at the edge would leave that path bare.
Security response headers set by this origin, their values and the reasoning for each
HeaderValueWhy
Content-Security-PolicySee the directive table belowDerived from what this site actually loads, rather than copied from a template. Every directive is either the floor or a specific concession with a reason.
X-Content-Type-OptionsnosniffThe browser honours the declared content type instead of guessing one. A guess is how a file that is served as text ends up being executed as script.
Referrer-Policystrict-origin-when-cross-originA site you navigate to is told which origin you came from, never which page. A path can identify an individual, and a referrer leaks it accidentally.
X-Frame-OptionsDENYThe legacy counterpart of frame-ancestors, kept for user agents that honour only the older header. Two mechanisms saying the same thing is cheap insurance.
Permissions-Policycamera=(), microphone=(), geolocation=(), payment=(), usb=()An empty allowlist for capabilities this property has no use for. Nothing on the page, or embedded in it, can prompt you for any of them.
Strict-Transport-Securitymax-age=63072000; includeSubDomainsTwo years, subdomains included. Deliberately NOT preload: submission to the preload list is a slow-to-reverse commitment binding the whole domain, and it is the operator's decision to make explicitly rather than a side effect of a config file.

The policy, directive by directive

The Content-Security-Policy is derived from what this site loads.

Two concessions are made and both are named. Everything else is the floor.
Every directive in the Content-Security-Policy and the reason it carries the value it does
DirectiveWhy it is this
default-src 'self'The floor. Anything not named by a more specific directive may only come from this origin.
base-uri 'none'Nothing may rewrite the document base. An injected base element silently re-points every relative URL on the page, including the acquisition links.
object-src 'none'No plugin content. Nothing on this property needs it, and the legacy embedding surface is worth closing outright.
frame-ancestors 'none'This site is never to be framed. A framed page can be dressed as a step inside somebody else's flow, which is exactly how a handoff gets impersonated.
form-action 'self'A form may only submit to this origin. The TrustOps handoff is a top-level navigation from a link, which this directive does not govern.
script-src 'self' 'unsafe-inline'Next.js inlines its bootstrap and flight payload as inline script. Removing 'unsafe-inline' requires per-request nonces, which requires middleware, which forces every prerendered page to become dynamic. 'unsafe-eval' is deliberately absent — no code path here builds code from a string.
style-src 'self' 'unsafe-inline'next/font injects an inline style element. Same trade as above, with a smaller blast radius: a style is not an execution context.
img-src 'self' data: https://api.ecocitizenz.comExactly one remote origin, named rather than wildcarded, because it draws the Live Proof badge from current state. A wildcard on the parent domain would admit any subdomain somebody could stand up.
font-src 'self'next/font self-hosts the typeface at build time, so no font origin is contacted at runtime and no third party learns who is reading.
connect-src 'self'The browser may not open a request to any other origin. This is what makes the privacy statement structural rather than procedural: a third-party collector could not be reached even if one were added by mistake.
manifest-src 'self'The web app manifest is served from this origin, like everything else it describes.
upgrade-insecure-requestsAny plaintext subresource that slipped into a template is fetched over https instead of being requested in the clear.

The absence that matters most

'unsafe-eval' is not in the script policy and no build step needs it. Inline script is permitted because the framework emits its bootstrap that way; building executable code from a string is a different capability and one this site never uses.

Build-time preflight

A production build fails rather than ship a call to action pointing at the wrong host.

Every acquisition, resolver and badge base is validated against the value that would actually ship, not against the presence of a variable name.

It must be a valid absolute URL

A malformed value would otherwise resolve against this origin at runtime and produce a link that goes nowhere, discovered by an operator at the worst possible moment.

It must be https

An acquisition or proof handoff sent over plaintext is one an intermediary can rewrite. There is no development exception in the shipped value.

Its host must be on an enumerated allowlist

Five hosts, listed one by one rather than matched by pattern. A wildcard on the parent domain would admit any subdomain an attacker could stand up, which defeats the point of checking at all.

The contact address must be real

It must parse, and it must not be a placeholder domain. A disclosure route that bounces is worse than one that is obviously missing.

The reason the build is willing to fail over this: a call to action pointing at an unexpected host is indistinguishable, to the person clicking it, from a hijacked one. The free Passport link is the most important link on this property, and a silently degraded handoff is invisible until a real operator follows it.

A production build throws. A development build warns loudly and carries on, so local work is never blocked by configuration that only matters at release. The defaults are the real production values, so an unset variable degrades to correct rather than to broken.

What none of this establishes

Headers constrain a browser. They are not a claim about outcomes.

No certification, no audit, no scheme

This page describes configuration you can verify yourself by reading the response headers of any page on this host. It does not assert an external assessment, a compliance standard or an accreditation, because there is none to assert.

No bug-bounty programme

There is no reward process, no scope document and no published response-time commitment. We would rather say that than invent a programme that does not exist behind the address below.

A policy is a constraint, not a promise

A restrictive policy narrows what a browser is permitted to do with this document. It is not a claim that no defect exists here or anywhere else in the estate, and no configuration is a substitute for that honesty.

And it says nothing about any agent

How this website is configured has no bearing on any agent that holds an ECZ-ID. A published record establishes identity and an operator relationship, and nothing on this page extends it.

How to read a record

Responsible disclosure.

If you have found something wrong with this property, write to us directly. One address, monitored, with no form in front of it.

  • Say which surface

    This site, TrustOps, the Resolver or the badge API. They are operated as separate surfaces and a report that names the wrong one takes longer to act on.

  • Include the request

    The URL, the method, the response you saw and the steps to reproduce it. A description of an impact without a reproduction is difficult to act on quickly.

  • Please do not include third-party data

    Send us what demonstrates the issue, not somebody else's personal data. If a report would require it to be understood, say so and we will work out how to look at it.