Oracle Cloud is the least written-about of the four and the one where the documentation is least likely to match what you actually receive. This post is about the two places it differs from the others in ways that matter: how alarms reach you, and how requests are authenticated.
Alarms arrive through Notifications
OCI Monitoring alarms do not post directly. They publish to a Notifications topic (ONS), which delivers to a subscription — the same indirection as SNS on AWS, including the subscription confirmation handshake.
alarm"] --> T["Notifications
topic"] T --> S["HTTPS
subscription"] S --> W["/api/webhooks/oci"] S -.->|"first message"| C["ConfirmationURL"] C -.->|"reported,
never fetched"| P(["a person confirms it
once they recognise the topic"]) W --> N["One normalized incident"] style P fill:#fff4e2,stroke:#f4a03a,stroke-width:2px style N fill:#f4f6ff,stroke:#a78bfa
Same rule as SNS, for the same reason: the ConfirmationURL is reported rather
than fetched. Auto-confirming would let anyone who can reach the endpoint make the API issue
a GET to a host of their choosing, and no amount of "but it's from Oracle" in the payload
changes that — the payload is exactly the thing an attacker controls.
The payload, and the transition field
The field that carries the state change is type, not a status:
| Value | Means | Handled as |
|---|---|---|
OK_TO_FIRING | The alarm just started | Opens an incident |
REPEAT | Still firing, periodic reminder | Deduplicated onto the open one |
FIRING_TO_OK | Cleared | Resolves the incident |
RESET | Alarm reset | Resolves the incident |
The alarm itself sits in alarmMetaData[0] — an array, with the OCID, the
MQL query, the namespace, the dimensions and a link back to the console. Dimensions become
labels, and resourceDisplayName becomes the service, which is what match rules
bind to.
Timestamps are epoch milliseconds
timestampEpochMillis is exactly what it says. Handed to a parser that
assumes seconds it produces a date in the year 55,000 — which sorts strangely,
does not error, and is the kind of bug you find three weeks later while wondering why
one incident is always at the top of the list. Opsgenie's createdAt has the
same shape and the same trap.
Request signing, done by hand
OCI does not issue bearer tokens. Every request is signed with your API key over a named set of headers — the HTTP Signatures draft. It is the fussiest of the four auth schemes and the most clearly specified.
What gets signed: the request target, host and date for a GET; plus content length, content type and a SHA-256 of the body for a POST. That last part is what stops a signed request being replayed with different contents.
(request-target): post /20180401/metrics/actions/summarizeMetricsData?… host: telemetry.{{region}}.oraclecloud.com date: Fri, 26 Sep 2026 11:04:22 GMT content-length: 184 content-type: application/json x-content-sha256: Zm9vYmFy… → RSA-SHA256 over the above, base64, into: Authorization: Signature version="1",keyId="{tenancy}/{user}/{fingerprint}", algorithm="rsa-sha256",headers="…",signature="…"
Roughly forty lines with cryptography, which the image already carries for its
own JWTs — so no OCI SDK. Five environment variables: OCI_TENANCY,
OCI_USER, OCI_FINGERPRINT, OCI_KEY_FILE,
OCI_REGION. Missing any one, the steps report unavailable and name
what is absent rather than failing obscurely inside a signature.
Two read steps
| Step | What it does |
|---|---|
oci.metrics | An MQL query against Monitoring — CpuUtilization[1m].mean() in a namespace and compartment. |
oci.logs | An OCI Logging search over a window. |
cloud.health | Provider-side issues, filtered to your region. See below — this one had a surprise in it. |
The status feed is not a status page
This is the one part of the OCI connector verified against the live service rather than against documentation, because the feed is public — and it was not what we expected.
Most providers expose a Statuspage-style incidents.json. OCI does not. It
publishes components.json: a single document containing every region
— ninety of them — each with every service and that service's current
status. There is no flat list of open incidents to read.
~90 regions"] --> R{"Filter to
OCI_REGION"} R -->|"region matches"| S["walk its serviceHealthReports"] R -->|"no such region"| E["report it and skip —
check OCI_REGION"] S --> Q{"serviceStatus"} Q -->|"NormalPerformance"| I["ignore"] Q -->|"anything else"| O["an open event, with
the service and status"] style E fill:#fff4e2,stroke:#f4a03a style O fill:#fde8e8,stroke:#e57373
So the step filters to the region the deployment actually runs in. Without that filter an incident in Sydney would be reported to someone in Ashburn as though it were theirs — which is worse than saying nothing, because it sends an engineer to blame the provider for an outage on another continent. A region name that does not appear in the feed is reported as a configuration problem rather than silently returning "all clear".
Getting it wired
- A Notifications topic with an HTTPS subscription to
/api/webhooks/oci?token=…. Confirm it by hand from the first message. - Point your alarms at the topic and give them names you would be happy to see as alert names, because that is what they become.
- An API key for a read-only user, with a policy allowing
read metricsandread log-contentin the compartments you care about. - Set
OCI_REGIONto the canonical name —us-ashburn-1, not "Ashburn". The health step validates it and will tell you if it is wrong.
Stated plainly
The alarm normalizer and the two read steps are built to Oracle's published APIs and tested through the product, but not against a live tenancy. The signing implementation follows the documented scheme; the first real request is the test, and a signature failure is loud rather than subtle. The health step is verified live.
Next in this series
One on-call rota, four clouds — what stays the same across all of them, what genuinely differs, and where ServiceNow fits underneath.