secrets, holding the API key. Create it with the TenantClient. The tail is the per-map local name; the host stores it as z:<tid>:<tail>.
readers must be set explicitly — the KV governor defaults to deny, so leaving it off makes the contract’s own secret read fail with AccessDenied. MapAlreadyExists is idempotent — safe to re-run when re-deploying.
Map visibility quick reference:
"private"— only your contracts can access this map (default, use it for everything sensitive)."public"— world-readable via/api/dev/public-kv/<tid>/<tail>. Map tail must start withpublic:. Never put PII in a public map.
writers/readers restrict your contracts — including ones you deploy yourself — not you. As the map’s owner you can always write its entries directly via the control plane (tenant.executeControl("map-entry-set", …)), even on a writers: { only: [contractId] } map, or add a contract to writers via tenant.maps.update if you’d rather it write directly — that’s how seeding the API key works. A contract-only map is not tamper-proof against its owner; see Storage Namespaces → Access model.