Cross-organization threat sharing

An opt-in shared pool of indicators on Enterprise: the four actions, the three-reporter floor, what the platform does and does not do about hashing, and the dashboard tab.

Last reviewed September 16, 2026Fresh

Overview

Organizations on PasskeyBridge can pool indicators of compromise in one shared pool. An indicator surfaces in the feed only once several organizations have reported the same value independently, so reading the feed tells you that at least that many organizations saw it, and not which ones.

Who can use it. Enterprise only: the function calls the Enterprise plan gate and answers 403 with code: "plan_required" otherwise, and the dashboard tab shows an upgrade panel. contribute and subscribe additionally require the admin role on the organization; feed and status are open to any member.

Auth lane. Console-only, POST with a dashboard session. shield-threat-sharing is absent from the worker allowlist, so there is no public URL. The function resolves your organization from your own membership rows and ignores any tenant_id you put in the body; a user who belongs to several organizations cannot choose which one acts here.

Opt in first. Both contribute and feed require a subscription with sharing enabled, otherwise they answer 403 Enable threat sharing before contributing or 403 Enable threat sharing to access the feed. Turning sharing off stops both directions.

Four actions: contribute, feed, subscribe, status. Anything else answers 400 Unknown action, and a non-POST method answers 405 Method not allowed.

Actions and responses

contribute. Submit up to 100 indicators in one call. Admin only.

{
  "action": "contribute",
  "indicators": [
    { "indicator_hash": "9f2c...at least 16 characters", "indicator_type": "phone_hash", "severity": "high", "source_signal_type": "sim_swap" }
  ]
}

Each entry needs indicator_hash (string, 16 characters or more) and indicator_type from the ten values below. severity is optional and defaults to medium; source_signal_type is optional free text. A new value inserts a row with a contributor count of 1; a value already in the pool increments the count atomically unless your organization already reported it, in which case it is skipped. The reply is {"contributed": 3, "skipped": 1}, and a threat_sharing.contribute audit entry records the totals.

indicator_typeDashboard label
ip_hashIP address
email_hashEmail
device_hashDevice
credential_hashCredential
phone_hashPhone
sim_hashSIM signal
agent_hashAgent ID
domain_hashOther
fingerprint_hashOther
session_hashOther

The last three are accepted by the API but have no label in the dashboard table, so they display as "Other".

feed. Returns indicators at or above your reporter threshold, newest activity first.

{ "action": "feed", "limit": 100, "severity": "critical", "indicator_type": "phone_hash" }

limit defaults to 50 and is capped at 200. Both filters are optional and validated against the same value lists. There is no age filter: the feed is the whole qualifying pool, ordered by last_seen_at.

{
  "indicators": [
    { "id": "<uuid>", "indicator_hash": "9f2c...", "indicator_type": "phone_hash", "severity": "high", "contributor_count": 5, "first_seen_at": "...", "last_seen_at": "...", "metadata": {} }
  ],
  "own_contribution_ids": ["<uuid>"],
  "threshold": 3
}

subscribe. Admin only. {"action":"subscribe","sharing_enabled":true,"auto_contribute":false,"min_contributor_threshold":5} answers {"ok":true,"sharing_enabled":true} and writes a threat_sharing.subscribe or threat_sharing.unsubscribe audit entry. auto_contribute is stored, but no code reads it: nothing contributes on your behalf today, and every indicator in the pool got there through an explicit contribute call.

status. {"action":"status"} answers with your subscription, your own contribution count, the number of indicators currently above your threshold, and a severity breakdown of them.

StatusBodyCause
400{"error":"indicators array required"}empty or missing array
400{"error":"Max 100 indicators per request"}more than 100 entries
400{"error":"Each indicator must have a valid indicator_hash (min 16 chars)"}short or missing value
400{"error":"Invalid indicator_type: ... Allowed: ..."}type outside the ten
400{"error":"Invalid severity: ... Allowed: critical, high, medium, low"}unknown severity
401{"error":"Missing authorization"} or {"error":"Invalid auth token"}no session
403{"error":"Admin role required"}member without the admin role on contribute or subscribe
403{"error":"No tenant membership"}the caller belongs to no organization

Reporter threshold

An indicator is withheld until enough separate organizations have reported it. The floor is 3 and it cannot be lowered: the feed filter uses the larger of your min_contributor_threshold and 3, so setting 1 or 2 has no effect while setting 5 makes your own feed stricter than the floor.

The reason for the floor is what a smaller number would leak. At 1, the feed would show that one organization saw a specific value. At 2, an organization that knows it contributed can deduce that exactly one other did. At 3, a reader learns only that at least three organizations saw it.

contributor_count on each feed row tells you how many organizations reported that indicator, and own_contribution_ids tells you which rows are yours, which is how the dashboard shows the "You" badge. Neither reveals who the others are; no identifier of a contributing organization is returned in any response.

Your own contributions are always visible to you in your contribution count, whether or not the indicator has reached the threshold.

Hashing and privacy

Hashing is the contributor's job, and the platform does not check it. The function validates that indicator_hash is a string of at least 16 characters and that the type is known. It does not hash anything, and it cannot tell a digest from a plaintext value. Anything you submit is stored and shown to other organizations exactly as you sent it.

That makes the digest you choose the whole of the privacy guarantee, and a plain SHA-256 is not enough for these fields. Phone numbers, email addresses and IP addresses are drawn from small, enumerable spaces: an attacker with the feed can hash every number in a country's range and recover the original in minutes. Submit a keyed digest instead, computed under a secret your organization holds, for example HMAC-SHA-256 of the normalized value under a shared pepper agreed with the organizations you exchange with. That is the same discipline PasskeyBridge applies to identifiers it stores itself, described in Zero-PII compliance.

Practical rules for a contributor:

  • Normalize before you digest (E.164 for phones, lower-case for email) so two organizations reporting the same subject produce the same value.
  • Never submit a raw identifier, a reversible encoding of one, or a plain digest of a low-entropy field.
  • Keep source_signal_type generic. It is free text, it is stored, and it is not shown in the dashboard feed today.
  • Remember that a contribution cannot be withdrawn through the API: there is no delete action, and the reporter count of an indicator you contributed to does not go down.

What is shared with other organizations: the value you submitted, its type, its severity, the number of reporting organizations, and the first and last time it was seen. What is not: your organization's identity, your event rows, or any field of the signal the indicator came from.

Threat sharing tab

Dashboard > Threat sharing, in the Security group, on Enterprise.

  • Sharing switch in the tab header turns the subscription on and off. Everything else stays empty until it is on.
  • Four tiles: Shared indicators (the pool above your threshold), Contributed (your own count), Critical (critical-severity indicators above the threshold), Minimum reporters (your threshold).
  • Sharing settings, a collapsed section with an Automatic contribution switch and the Minimum reporters value. The switch stores the preference; no code acts on it today, so contribution stays manual.
  • Shared feed, a collapsed section with severity filter buttons (All, Critical, High, Medium, Low), a refresh control, and a table of Type, Indicator hash, Severity, Reporters, First seen, Last seen and Source. Rows you contributed carry a "You" badge; the rest show "Network". The table has no free-text search and no pagination beyond the 100-row fetch.

There is no contribute form in the dashboard. Submitting indicators means calling the contribute action from your own code with an admin session. The tab is for reading the feed and managing the subscription.

Confirm it worked. After a contribute call, the Contributed tile increases by the number reported as contributed, and Dashboard > Audit log shows a threat_sharing.contribute entry with the contributed and skipped counts.

Related from the blog