Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b8612a8
Oracle OCI Always Free: cited allowance (extdeps), allowance fit + fa…
Oct 5, 2026
2375ae9
OCI: tenancy home region us-ashburn-1 (operator statement 2026-10-05)…
Oct 5, 2026
70c7a84
OCI request signing (pure): signing string, body digest, HTTP date, A…
Oct 5, 2026
ceccc34
OCI compartment converge: signed curl transport, custody + accessor r…
Oct 5, 2026
c74c82d
OCI compartment ensure: enumerate JsonValue arms (no wildcard over a …
Oct 5, 2026
1b7fd99
OCI signing: body digest through the std sha256_hex_of_text seam (pur…
Oct 5, 2026
9c9cefa
OCI: address review on #13335 (all five findings)
Oct 5, 2026
6ba202d
OCI: unit carriers per review 76355 — OciOtherUsage memory/block as G…
Oct 5, 2026
b019c6c
OCI: rename OciOtherUsage.micros -> e2_micro_instances (review 76364:…
Oct 5, 2026
4962de1
extdeps.cloud.oracle_oci: drop every unconsumed declaration (bandwidt…
Oct 5, 2026
5b429f6
OCI signing: HTTP date over extdeps.units.iso8601_calendar (civil dat…
Oct 5, 2026
81136e1
OCI signing: content-length is the UTF-8 byte count via std.bytes (dr…
Oct 5, 2026
2dc8a7f
OCI: every create carries opc-retry-token derived from the exact requ…
Oct 5, 2026
090cb40
OCI: curl timeout is a caller-supplied Second (oci_request_timeout), …
Oct 5, 2026
6000600
OCI: zero-priced binding unavailable until plan-relative allocation +…
Oct 5, 2026
b867c92
Merge remote-tracking branch 'origin/main' into session/nimble-heron-805
Oct 6, 2026
805d2f4
Merge origin/main into session/nimble-heron-805; Absent/Present from …
Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/fleet-converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ on:
credential:
description: "host_credential_custody_converge only: which rostered custody row to deliver (gunbc.host_credential_custody_converge). A closed choice, never a free-text secret name: each option carries its own SecretRef, path, owner, group, mode and directory, and a row scoped to one host refuses any other"
required: false
options: [controller_app_key, approval_ntfy_publisher_token, fabric_state_writer_key, claude_code_oauth_harness_token]
options: [controller_app_key, approval_ntfy_publisher_token, fabric_state_writer_key, oracle_oci_api_signing_key, claude_code_oauth_harness_token]
type: choice
d0_consent:
description: "pair_serving_d0 only (Cut D, Group A on srv1; expected_revision is required and frozen with the filing): the escalation id the consent is filed under. It names the transaction, and a rerun after a crash MUST name the same id to find its frozen filing and held claim -- a fresh id is a new consent"
Expand Down
174 changes: 174 additions & 0 deletions dag/extdeps/cloud/oracle_oci.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
module extdeps.cloud.oracle_oci

import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }
import std.measure { Gigabyte, gigabyte }
import std.types { NonEmptyStr }
import std.nat { Nat }

// ORACLE CLOUD INFRASTRUCTURE COMPUTE, AS ITS ALWAYS FREE ALLOWANCE AND ITS TWO FREE SHAPES.
//
// Every figure below was read on 2026-10-05 from the three Oracle pages cited by the authority rows
// in this module, and each row names the page it came from. This module holds the vendor's facts
// only. Whether a planned set of instances fits the allowance, and how an instance becomes a fabric
// offer, are OUR questions, and they live in product.supplier.oracle_oci.
//
// THE ALLOWANCE WAS HALVED IN 2026, AND THE OLD FIGURE IS STILL WIDELY REPEATED. The Always Free page
// read on 2026-10-05 gives 1,500 OCPU hours and 9,000 GB hours a month, "equivalent to 2 OCPUs and
// 12 GB of memory". The 4 OCPU / 24 GB figure that many guides still give is the previous allowance.
// Only the current page is cited here. The secondary reports of the cut (heise, linuxiac) are not
// cited, because the vendor page states the current number directly.

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "docs.oracle.com/en-us/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm"
}
}

// A named handle on the Always Free page, for consumers that cite where an allowance figure came from
// (the same reason extdeps.cloud.ubicloud gives for ubicloud_runner_types_authority).
data oci_always_free_resources_authority: ExternalAuthority = extdeps_external_authority_anchor

// Two further Oracle pages are read by this module and cited in the notes that use them: the compute
// shapes page (docs.oracle.com/en-us/iaas/Content/Compute/References/computeshapes.htm) and the
// preemptible page (docs.oracle.com/en-us/iaas/Content/Compute/Concepts/preemptible.htm).

// ── THE TWO ALWAYS FREE SHAPES ────────────────────────────────────────────────────────────────
//
// There is no "Ampere Micro". The Arm option is one flexible shape whose size the tenant chooses, and
// the AMD option is one fixed micro shape. These are separate allowances, so a tenancy can use both
// at once.
type OciAlwaysFreeShape
= VmStandardA1Flex
| VmStandardE21Micro

fn oci_shape_wire_name(shape: OciAlwaysFreeShape) -> NonEmptyStr {
match shape {
VmStandardA1Flex => "VM.Standard.A1.Flex" as NonEmptyStr
VmStandardE21Micro => "VM.Standard.E2.1.Micro" as NonEmptyStr
}
}

// From the compute shapes page: "1 OCPU on Arm A1 (Compute) = 1 core on Arm A1 (Compute) or 1 vCPU",
// and "1 OCPU on x86 (AMD and Intel) = 2 vCPUs". A1.Flex is Aarch64 and E2.1.Micro is X86_64. These
// enter as typed rows with their consumer, the fabric offer shape, when a zero-priced offer can be
// minted (product.supplier.oracle_oci oci_tenancy_allocation_unread_obligation).

// The compute shapes page names the A1.Flex processor as Ampere Altra Q80-30, which is the catalog row
// extdeps.cpu.ampere altra_q8030_catalog. No consumer here needs the processor, so the row is named in
// this note rather than bound to a declaration nothing reads.

// ── THE ALLOWANCE ─────────────────────────────────────────────────────────────────────────────
//
// The page writes memory as "GB" and does not say whether that is decimal or binary. The figures are
// kept as the page writes them, as Gigabyte counts, and are not converted to bytes. A byte figure
// would assert a basis the vendor never stated.
//
// The page states the allowance as a monthly meter, "the first 1,500 OCPU hours and 9,000 GB hours per
// month for free for VM instances using the VM.Standard.A1.Flex shape", and then gives its steady-state
// equivalent: "For Always Free tenancies, this is equivalent to 2 OCPUs and 12 GB of memory." Only the
// equivalent is carried as a field, because it is the only one anything here consumes (the allowance
// fit in product.supplier.oracle_oci). The monthly meter enters as typed fields together with its
// consumer, an observation of the tenancy's month-to-date A1 consumption, which
// product.supplier.oracle_oci oci_tenancy_allocation_unread_obligation names as unread. Carrying it
// before then would be a declaration nothing reads.
type OciA1FlexAllowance {
always_free_ocpus: Nat
always_free_memory: Gigabyte
}

data oci_a1_flex_allowance: OciA1FlexAllowance = OciA1FlexAllowance {
always_free_ocpus: 2,
always_free_memory: gigabyte(count: 12),
}

// "All tenancies get up to two Always Free VM instances using the VM.Standard.E2.1.Micro shape".
// Each one: "1/8th of an OCPU with the ability to use additional CPU resources", 1 GB of memory, and
// "up to 50 Mbps network bandwidth via the internet". The count and the memory are consumed (the fit
// and the offer shape). The bandwidth is not, so it stays in this note until a consumer, such as a
// network-capacity term in placement, needs it as a std.measure Bandwidth.
type OciE2MicroAllowance {
max_instances: Nat
memory_per_instance: Gigabyte
}

data oci_e2_micro_allowance: OciE2MicroAllowance = OciE2MicroAllowance {
max_instances: 2,
memory_per_instance: gigabyte(count: 1),
}

// "All tenancies receive a total of 200 GB of Block Volume storage, and five volume backups". Every
// instance's boot volume is a block volume, so this one pool is shared by every free instance. The
// backup count is not consumed until something takes volume backups, so it stays in this note.
type OciBlockVolumeAllowance {
total: Gigabyte
}

data oci_block_volume_allowance: OciBlockVolumeAllowance = OciBlockVolumeAllowance {
total: gigabyte(count: 200),
}

// ── PLACEMENT RULES ───────────────────────────────────────────────────────────────────────────
//
// "You must create the Always Free compute instances in your home region." The home region is a fact
// about one tenancy, not about Oracle, so it is never written here. The consumer has to supply it.
//
// "Instances using the VM.Standard.E2.1.Micro shape can only be created in one availability domain",
// while A1 instances may use any availability domain, except in South Korea North (Chuncheon). Which
// domain admits the micro is a per-tenancy fact, so the consumer reads it from the API's shape list
// (gunbc.oracle_oci.instance_ensure) and plans carry it as their micro placement.
// The five capacity types the instance-creation console offers. The Always Free page describes only
// ordinary instances. It says nothing about the other four, so the free-eligibility of those four is
// UNREAD, which is different from refused. The preemptible page states that preemptible capacity
// "costs 50% less than on-demand capacity", that A1.Flex supports it, and that E2.1.Micro does not.
// It also says nothing about Always Free.
type OciCapacityType
= OnDemandCapacity
| PreemptibleCapacity
| CapacityReservation
| DedicatedVirtualMachineHost
| ComputeCluster

// ── IDLE RECLAMATION ──────────────────────────────────────────────────────────────────────────
//
// "Oracle will deem virtual machine and bare metal compute instances as idle if, during a 7-day
// period, the following are true: CPU utilization for the 95th percentile is less than 20%, Network
// utilization is less than 20%, Memory utilization is less than 20% (applies to A1 shapes only)".
// All the conditions must hold together, so an instance busy on any one axis is not idle, and on
// E2.1.Micro the memory condition does not apply. Nothing here observes utilization yet. The rule
// enters as typed rows together with its consumer, a utilization observation of a running instance
// judged against them.

// ── ACCOUNT TIERS ─────────────────────────────────────────────────────────────────────────────
//
// The allowance is stated for "all tenancies", so a Pay As You Go tenancy gets the same free amounts.
// What differs is what happens above them. On an Always Free tenancy, resources beyond the allowance
// are simply not available. The out-of-capacity guidance says that upgrading "gives you access to
// more types of Compute resources". On a paid tenancy, usage above the allowance is billed at that
// resource's rate. No rate is read into this module, so a consumer that needs the overage price must
// refuse rather than assume one.
type OciTenancyTier
= AlwaysFreeTenancy
| PayAsYouGoTenancy

// ── REGIONS ───────────────────────────────────────────────────────────────────────────────────
//
// One row per region a consumer has needed. Read 2026-10-05 from the regions page,
// docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm:
// US East (Ashburn), identifier us-ashburn-1, region key IAD, realm OC1, three availability domains.
// The domain count matters for the micro rule above, because exactly one of the three can host an
// E2.1.Micro.
type OciRegion {
identifier: NonEmptyStr
region_key: NonEmptyStr
realm_key: NonEmptyStr
availability_domain_count: Nat
}

data oci_region_us_ashburn_1: OciRegion = OciRegion {
identifier: "us-ashburn-1",
region_key: "IAD",
realm_key: "OC1",
availability_domain_count: 3,
}
Loading
Loading