Documentation
¶
Overview ¶
Package substrate is the contract GitHub issue #1118 asks for between this fork's live path and a provider family: one Substrate value per family (AWS, Kubernetes) that answers which marker surface a schema carries, how the marker is read off a live or planned object, how it is written, and which sweep client the family's provider block builds.
Before it, those answers were three dispatches that each asked the internal/live/markers predicates on their own: the projection's ownership read (markerSurfaceOf and markersOf), live-mv's surfaceOf, and live-import's ratifyOne carriers. Each was found missing a surface after the unit that should have covered it had merged (#1108, #1104, #1109). They now ask this package, and this package is the one place a new surface has to be taught. The completeness guard in internal/live/markers/seams_test.go measures the functions here the same way it measures every other seam, so a fourth surface fails every dispatch below until it is handled.
It is an extraction: every answer here is the answer the dispatch it replaced gave. GitHub issue #1589: the projection's ownership read used to ask a looser tag question than SurfaceOf (markers.HasTagsAttribute (since deleted)), kept apart in case the two ever disagreed on a real AWS or Kubernetes type. The 2026-09-26 decision package measured the disagreement empty at every pinned provider version, so the ownership read now asks SurfaceOf like every other caller and the second question is gone.
Index ¶
- Constants
- Variables
- func ARMIDSegment(parts []ARMIDPart, insensitive bool, id, segment string) (string, bool)
- func ARMIDType(id string) (string, bool)
- func ARMTagScope(obj cty.Value) string
- func AddressCarrier(surface markers.Surface) (key, noun string)
- func AddressInMarkers(surface markers.Surface) bool
- func AzapiRecordArguments(block *configschema.Block, obj cty.Value) (map[string]string, bool)
- func AzapiResourceShape(block *configschema.Block) bool
- func AzapiTypeCarriesMarker(typ string) bool
- func AzureSweepAttrs(val cty.Value, ok bool) azsweep.Attrs
- func AzurermAdmission() map[string]int
- func AzurermCloudIdentityAttrs() map[string]string
- func AzurermIDSource() (version, commit string)
- func AzurermRecordImportID(typeName string, values map[string]string) (string, bool)
- func AzurermRecordable(typeName string) bool
- func AzurermWithheld() [][2]string
- func AzurermWithheldIn(raw []byte) (map[string]string, error)
- func CarrierPaths(surface markers.Surface) []cty.Path
- func CarrierPhrase(surface markers.Surface) string
- func CarriesAddress(surface markers.Surface) bool
- func CreateCollidesOnKey(surface markers.Surface) bool
- func CreateOverwritesAtKey(surface markers.Surface) bool
- func CreatedObject(surface markers.Surface, created Created) string
- func FieldGranularNamesKind(block *configschema.Block) bool
- func FieldGranularShape(block *configschema.Block) (namespaced bool, ok bool)
- func KnownMarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
- func KubernetesSweepAttrs(val cty.Value, ok bool) kubesweep.Attrs
- func ManualMarkFix(surface markers.Surface, created Created, want map[string]string, facts Facts) string
- func MarkerNoun(surface markers.Surface) string
- func MarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
- func NameHeldDetail(surface markers.Surface, typeName, importID string, ...) (string, bool)
- func NotACarrier(providerType string, block *configschema.Block, typeName string) string
- func ObjectMetaShape(block *configschema.Block) (namespaced bool, ok bool)
- func ObjectString(obj cty.Value, name string) string
- func PostCreateNeeded(surface markers.Surface, created Created, facts Facts) (string, bool)
- func RecordsRemoveUnrowed(providerType string) bool
- func SurfaceOf(providerType string, block *configschema.Block) (markers.Surface, bool)
- func Sweeps(providerType string) bool
- func TombstonesRead(providerType string) bool
- type ARMIDPart
- type AWSCreateTagFacts
- type AssetSearchSweeper
- type Created
- type Facts
- type Hold
- type HoldEvidence
- type HoldRecognition
- type IdentityComponent
- type IdentitySource
- type LabelListSweeper
- type ResourceGraphSweeper
- type Substrate
- type Sweep
- type Sweeper
- type SynthesizedIdentity
- type Unrecorded
- type Write
- type Writes
Constants ¶
const ( AzapiRecordType = "type" AzapiRecordParentID = "parent_id" AzapiRecordName = "name" )
The azapi_resource arguments a record keeps, named as the provider names them.
const ( // AzurermRouteIdentitySchema: the provider's resource identity schema // admits the type on its own (internal/live/identity's // IdentitySchemaRouteAdmits), and its template, where it agrees, is // the rendered id. AzurermRouteIdentitySchema = "identity-schema" // AzurermRouteTemplateFallback: the type serves an identity schema the // route cannot satisfy from configuration, and its ARM id template, // which passes every guard here including azurerm's own identity test, // admits it. AzurermRouteTemplateFallback = "template-fallback" // AzurermRouteTemplate and AzurermRouteParentID: no identity schema, // admitted by the template alone. AzurermRouteTemplate = azurermClassTemplate AzurermRouteParentID = azurermClassParentID // AzurermRouteComposite: the id is the join of the type's linked // objects' ids as azurerm joins them (GitHub issue #2204). AzurermRouteComposite = azurermClassComposite // AzurermRouteWithheld: the type has a template and a guard refuses // it, and nothing else admits the type. Refused by name. AzurermRouteWithheld = "withheld" // AzurermRouteUnmapped: no template and no identity-schema admission. AzurermRouteUnmapped = azurermClassUnmapped )
The routes the generator records per type (#1120 unit 17), the field tools/readiness-gen reads as "route".
const ( ARMIDFixed = "fixed" ARMIDConstant = "constant" ARMIDValue = "value" )
The kinds of ARMIDPart.
const ( FieldManagerAttr = "field_manager" FieldForceAttr = "force" )
The schema attributes that make a type field-granular (GitHub issue #1191): a top-level server-side-apply field manager name and the force flag beside it. hashicorp/kubernetes 3.2.1 serves exactly six types with both - kubernetes_labels, kubernetes_annotations, kubernetes_env, kubernetes_config_map_v1_data, kubernetes_secret_v1_data and kubernetes_node_taint - and kubernetes_manifest, the seventh type that can name a field manager, names it in a nested block instead, so it is not this shape.
const AKSManagedTagPrefix = "aks-managed-"
AKSManagedTagPrefix is the prefix of the tags AKS writes on its node resource group and the resources in it. The AKS FAQ ("Can I modify tags and other properties of the AKS resources in the node resource group?", learn.microsoft.com/azure/aks/faq, read 2026-10-19) names the aks-managed tags as Azure-created and not to be modified. Neither provider's source writes or names them.
const AzapiImportSyntax = "PARENT_ID/providers/NAMESPACE/TYPE/NAME, or PARENT_ID/CHILDTYPE/NAME for a child type"
AzapiImportSyntax documents azapi_resource's identity: the ARM id azapi builds from its three arguments (internal/services/parse/resource.go's newResourceID at azapi 2.13.0), which is also its import id.
const AzurermIDsPath = "azurerm-ids.json"
AzurermIDsPath is the embedded copy, relative to this package.
const FieldGranularImportSyntax = "apiVersion=APIVERSION,kind=KIND,[namespace=NAMESPACE,]name=NAME"
FieldGranularImportSyntax is the identity string a field-granular type that names its kind renders: the shape hashicorp/kubernetes' own kubernetes_env Read parses its id with (keys in any order; measured against 3.2.1 on 2026-10-03, where an id without apiVersion, kind and name is refused with "ID must contain apiVersion, kind, and name").
const ManifestImportSyntax = "apiVersion=APIVERSION,kind=KIND,[namespace=NAMESPACE,]name=NAME"
ManifestImportSyntax is the provider's documented import id for a manifest object, the namespace segment present for a namespaced kind only.
Variables ¶
var All = []Substrate{Kubernetes, Azure, Google, AWS}
All is every family, in the order an ordered question asks them.
AWS is last, and that is load-bearing for Substrate.SynthesizeIdentity (GitHub issue #1586): the AWS answer is the identity-schema route, which claims every type, so a family with a convention of its own has to be asked before it. TestTheCatchAllFamilyIsLast holds it (GitHub issue #1742). The surface questions are asked of the provider's own family (SurfaceOf), so for a claimed provider the order does not decide them.
var LifecycleOwnerTypes = map[string]bool{ "microsoft.containerservice/managedclusters": true, "microsoft.solutions/applications": true, }
LifecycleOwnerTypes are the ARM resource types whose id in an object's managedBy means that resource owns the object's lifecycle: it created the object and deletes it with itself, so the object is held, never swept (#1122, #2133 finding 2). Lowercased; ARM compares types case-insensitively.
- Microsoft.ContainerService/managedClusters on its node resource group. The AKS FAQ ("Why are two resource groups created with AKS?", learn.microsoft.com/azure/aks/faq, read 2026-10-20): "The AKS resource provider automatically creates the second resource group during deployment ... AKS automatically deletes the node resource group whenever you delete the cluster."
- Microsoft.Solutions/applications on its managed resource group. The Managed Applications overview ("Managed resource group", learn.microsoft.com/azure/azure-resource-manager/managed-applications/overview, read 2026-10-20): "When the customer deletes the managed application, the managed resource group is also deleted."
A resource group's managedBy is "The ID of the resource that manages this resource group" (Resource Groups - Get, ResourceGroup definition, learn.microsoft.com/rest/api/resources/resource-groups/get, api-version 2021-04-01). Neither page prints the managedBy value of a node or managed resource group; that is the maintainer's to measure.
Left out because no Microsoft page found on 2026-10-20 states their lifecycle tie, though azurerm creates each through a managed_resource_group_name argument: Microsoft.Databricks/workspaces, Microsoft.Synapse/workspaces, Microsoft.RedHatOpenShift/openShiftClusters, Microsoft.Purview/accounts and Microsoft.Workloads/sapVirtualInstances. An owner missing here costs a held group being read as an ordinary one, which matters only if it carries this estate's marker.
Functions ¶
func ARMIDSegment ¶ added in v0.31.0
ARMIDSegment parses id as an ARM id of the segments parts and returns the value of the segment named segment, the way go-azure-helpers' resourceids.Parser.Parse does for an id type with no scope segment (parse.go at the version azurerm 5.10.0 vendors): the id splits on "/" into exactly as many path segments as parts; a fixed part equals its text, a constant part equals one of its possible values, and a value part is any non-empty text. With insensitive false, as ParseXxxID and every ValidateXxxID parse, fixed and constant parts compare case-sensitively; with insensitive true, as ParseXxxIDInsensitively parses, they compare without regard to case. Nothing is matched by search: a segment is its position, so an id of any other shape, a wrong constant, or parts naming no such segment answer false.
One trailing "/" is accepted, as Parse accepts it: Parse trims each segment and the "/" after it off the input as it goes, and an input with nothing left over is whole.
func ARMIDType ¶ added in v0.31.0
ARMIDType is the ARM resource type an ARM id names: the namespace after its last /providers/ segment and every type segment after it, with the names dropped ("Microsoft.ContainerService/managedClusters" for a cluster's id, "Microsoft.Compute/virtualMachineScaleSets/virtualMachines" for a scale set instance's). Matched case-insensitively and returned as spelled. False when the id names no provider namespace, or the segments after it do not pair into type and name.
func ARMTagScope ¶ added in v0.31.0
ARMTagScope is the ARM id the Tags API write addresses for an object the provider returned: its id, the ARM resource id in both providers. One object is the exception, by rule rather than by type: an id outside any subscription (a tenant-level "/providers/..." path) names no taggable resource, and ARM writes tags only on a resource, a resource group or a subscription. Such an object that carries a subscription_id is tagged on that subscription, which is what azurerm_subscription does with its own tags: its id is the Microsoft.Subscription alias, and its Create tags commonids.NewSubscriptionID(subscriptionId) (subscription_resource.go, azurerm 5.10.0). Empty when the object names neither.
func AddressCarrier ¶ added in v0.21.0
AddressCarrier is surface's family's [Substrate.AddressCarrier]: the key the block address is written under and what one entry there is called. Empty for the zero Surface, which carries no address.
func AddressInMarkers ¶
AddressInMarkers reports whether surface's marker map holds the tofu-address key, which is its family's [Substrate.AddressInMarkers]. False for the zero Surface.
func AzapiRecordArguments ¶ added in v0.31.0
AzapiRecordArguments reads the three arguments azapi_resource builds its id from off an applied object of a block with azapi_resource's shape (AzapiResourceShape), for the record to keep. ok is false for any other block, or when one of the three is not a known, non-empty, unmarked string: a record with a hole in it is never written.
func AzapiResourceShape ¶ added in v0.31.0
func AzapiResourceShape(block *configschema.Block) bool
AzapiResourceShape reports whether block is azapi_resource's shape: a required string type, settable string parent_id and name, a settable tags map (markers.ARMTaggable), and no resource_id. Read from the schema rather than the type name alone, because azapi serves three more types carrying type and a parent or name (azapi 2.13.0): azapi_update_resource and azapi_resource_action act on an object they do not create, named by resource_id, and azapi_data_plane_resource is a data-plane object with no tags and no ARM id. None of them owns the object its arguments name, so none may be identified by it.
func AzapiTypeCarriesMarker ¶ added in v0.31.0
AzapiTypeCarriesMarker reports whether an azapi_resource whose `type` argument is typ was stamped with a marker: its ARM type takes tags at that API version by azapi's own type data (armtypes.Taggable). It is the stamp's own question (internal/live/projection's armTagTarget), asked again by a reader that has only the record: false for a type the table says is untaggable, for one it does not know, and for a value that does not parse, because the stamp writes nothing on any of them.
func AzureSweepAttrs ¶ added in v0.31.0
AzureSweepAttrs reads the connection arguments the Resource Graph sweep understands off an evaluated azurerm or azapi provider block (azsweep.ProviderAttrs), before the environment fallbacks. Which of the two providers the block configures is read off its shape, since Substrate.NewSweeper is handed the value alone: azurerm's provider schema has a features block and azapi's has none, and azapi's has an endpoint block and default_tags, which azurerm's has not (both providers' schemas at the pinned versions, azurerm 5.10.0 and azapi 2.13.0). A value of neither shape, or none at all, reads as azurerm, the family's name.
func AzurermAdmission ¶ added in v0.31.0
AzurermAdmission is how many azurerm types the embedded artifact gives each answer: "template" and "parent-id" for the two classes this family admits by template alone, "withheld" for the template-bearing rows of those classes the guards above refuse, "unmapped" for the rows with no template, and for the types with a resource identity schema "identity-schema+template" (handed a template that passes the guards) or "identity-schema" (none, or one the guards withhold).
func AzurermCloudIdentityAttrs ¶ added in v0.31.0
AzurermCloudIdentityAttrs is [azurermCloudIdentityAttrs] for tools/azurerm-id-gen, which asks the identity-schema route the same question this family does when it records each type's route (#1120 unit 17).
func AzurermIDSource ¶ added in v0.31.0
func AzurermIDSource() (version, commit string)
AzurermIDSource is the azurerm version and commit the embedded artifact was generated against.
func AzurermRecordImportID ¶ added in v0.31.0
AzurermRecordImportID rebuilds the ARM id of the azurerm object a record names from the values it holds, keyed by argument or identity attribute. Every segment has to be present: a hole is refused, never filled, because an id with a hole in it names a different object or none (HANDOFF's safety rule).
A record of an identity-schema type is keyed by identity attribute, and since GitHub issue #2138 holds attributes no argument is named after (the storage_account_name a container's record parses out of its storage_account_id), so where the row records where each attribute sits in the type's own id, that is what it is rebuilt from first.
func AzurermRecordable ¶ added in v0.31.0
AzurermRecordable reports whether a record of typeName can be turned back into its ARM id: the type has an id template AzurermRecordImportID reads. It says nothing about whether any one record holds every value.
func AzurermWithheld ¶ added in v0.31.0
func AzurermWithheld() [][2]string
AzurermWithheld is every template-bearing azurerm type the guards refuse, sorted, with the reason.
func AzurermWithheldIn ¶ added in v0.31.0
AzurermWithheldIn is the guards above run over an artifact that is not (yet) the embedded one: every template-bearing type in raw they refuse, with the reason. tools/azurerm-id-gen calls it on the artifact it is writing, so the route it records for a withheld row is this package's verdict rather than a copy of its rules (#1120 unit 17).
func CarrierPaths ¶ added in v0.21.0
CarrierPaths is surface's family's [Substrate.CarrierPaths]: the paths of the maps its marker lives in. Nil for the zero Surface.
func CarrierPhrase ¶
CarrierPhrase names where surface's marker map lives ("tags attribute", "metadata.labels map"), for the one refusal that has to tell an operator the provider returned no such map. Empty for the zero Surface, which has no map to be missing: a caller asks this only of a surface it read.
func CarriesAddress ¶
CarriesAddress reports whether an object on surface carries its block address, which is its family's Substrate.CarriesAddress. Both families do: AWS in the tofu-address tag, Kubernetes in the address annotation beside the estate label (GitHub issue #1641, step 3 of the ruling on #1605). False for the zero Surface.
On Kubernetes an object can still lack the annotation - one an older build created, or one migrated from stock state before live-import stamped it - so a reader that needs the address of one particular object asks that object, not this.
func CreateCollidesOnKey ¶
CreateCollidesOnKey reports whether, for a declared resource on surface, an object read at its identity means the resource's own create would be refused by the server as a duplicate: the first half of GitHub issue #1546's ruling, "its identity is a server-enforced unique key". False for the zero Surface.
True for the Kubernetes label surface only, and each family's answer says why its other surfaces are excluded ([kubernetes.CreateCollidesOnKey], [aws.CreateCollidesOnKey]).
func CreateOverwritesAtKey ¶ added in v0.31.0
CreateOverwritesAtKey reports whether, for a declared resource on surface, a create sent at an identity where an object already exists updates that object in place instead of failing: CreateCollidesOnKey's opposite case, and the dangerous one (GitHub issue #1120, ruled on #1122). A server that refuses the duplicate stops the plan by itself; one that accepts it hands the existing object, with whatever it holds, to the configuration that "created" it, with no estate marker ever read. So the projection refuses such a create by name before it is proposed (its SummaryOverwriteAtKey), the declared_untagged quadrant's default refusal made an error. False for the zero Surface.
True for Azure's arm-tags surface only: an ARM create is a PUT, which is create-or-update ([azure.CreateOverwritesAtKey]).
func CreatedObject ¶
CreatedObject is [Substrate.CreatedObject] asked of surface's family. A surface with no family gets the object's id, the one attribute every provider's object has.
func FieldGranularNamesKind ¶ added in v0.22.0
func FieldGranularNamesKind(block *configschema.Block) bool
FieldGranularNamesKind reports whether a field-granular schema names the patched object's apiVersion and kind in its own configuration (the top-level required api_version and kind of kubernetes_labels, kubernetes_annotations and kubernetes_env). The other three types patch one fixed kind, which the provider hardcodes.
func FieldGranularShape ¶ added in v0.22.0
func FieldGranularShape(block *configschema.Block) (namespaced bool, ok bool)
FieldGranularShape reports whether block is the field-granular Kubernetes shape (GitHub issue #1191, ruled 2026-10-03): a resource that writes some fields of an object it does not own, under a server-side-apply field manager it names itself. Read from the schema, never from a type-name list, for the same reason ObjectMetaShape is:
- a top-level optional string FieldManagerAttr and a top-level optional bool FieldForceAttr;
- a "metadata" nested list block of at most one item with a settable string "name", and WITHOUT the computed uid and the labels map ObjectMetaShape requires - the block names the patched object, it is not this resource's own object metadata.
The predicate and ObjectMetaShape can therefore never both answer true for one schema.
What the shape owns is fields, not an object, so its ownership marker is not a label: it is the field manager every write is made under, markers.FieldManagerFor's "choudoufu:<estate>", and the patched object's own estate label (if it has one) is irrelevant to it.
func KnownMarkersOf ¶
KnownMarkersOf is MarkersOf for a caller that must not read an unknown marker map as an empty one. It reports false when the object is null or unknown, when any carrier path reaches an unknown value (the map, or anything on the way to it), or when the surface reader itself reports false. A value beside the carrier being unknown, such as a planned object's metadata.resource_version, does not hide a marker that is known.
A carrier path that ends early at a null, or indexes past an empty list, is not unknown: the reader decides what that object carries.
func KubernetesSweepAttrs ¶
KubernetesSweepAttrs reads the connection arguments the Kubernetes sweep understands off the evaluated provider block. A marked value is left unread rather than unmarked - the same rule internal/command's projectionProviders.region applies to a sensitive region - EXCEPT for the three arguments that are themselves the credential, which are unmarked and read: see secret below, and GitHub issue #1527 for what leaving them unread cost. It moved here from internal/command with the sweep-client choice (GitHub issue #1118).
func ManualMarkFix ¶
func ManualMarkFix(surface markers.Surface, created Created, want map[string]string, facts Facts) string
ManualMarkFix is [Substrate.ManualMarkFix] asked of surface's family. A surface with no family - the zero Surface, or one For does not recognise - gets the generic sentence naming only the markers, since there is no family to name a command for.
func MarkerNoun ¶
MarkerNoun is what one entry of surface's marker map is called ("tag", "label"), or "marker" for the zero Surface.
func MarkersOf ¶
MarkersOf reads the marker map off an object from wherever surface keeps it. The second return is the surface reader's own: false means the object has no such map at all, which on a type whose schema declares one is a provider bug and never a licence to adopt. False for the zero Surface.
func NameHeldDetail ¶ added in v0.31.0
func NameHeldDetail(surface markers.Surface, typeName, importID string, addr addrs.AbsResourceInstance, estate string) (string, bool)
NameHeldDetail is surface's family's sentence for an unlabelled object holding the name a declared create would send, and false when the family has none of its own. The first GCP gauntlet run printed the Kubernetes sentence (API server, kind and namespace, one label) for a compute instance.
func NotACarrier ¶
func NotACarrier(providerType string, block *configschema.Block, typeName string) string
NotACarrier explains why a resource type of provider providerType has nowhere to carry an ownership marker, in its family's own words: the AWS "no tags map this configuration can set" (or the tags map the marker vocabulary cannot round-trip, markers.NotAMarkerSurface), and the Kubernetes "no metadata.labels map". A provider with no family gets a sentence that names no carrier rather than the AWS one. The caller has already established the type carries no surface (SurfaceOf false).
func ObjectMetaShape ¶
func ObjectMetaShape(block *configschema.Block) (namespaced bool, ok bool)
ObjectMetaShape reports whether block carries Kubernetes object metadata, and whether the kind it describes is namespaced. Read from the schema, never from a type-name list, for the same reason markers.Taggable is.
It is the shape internal/live/identity's metadata.go documents: a "metadata" nested block of list nesting with at most one item (declared by max_items, or by markers.UndeclaredSingleObjectMetadata where a plugin-framework schema cannot declare it), holding a settable "name", a computed "uid", a settable "labels" map, and - for a namespaced kind - a settable "namespace".
func ObjectString ¶ added in v0.22.0
ObjectString reads one top-level string attribute off obj, or "" when it is absent, null, unknown, marked or not a string.
func PostCreateNeeded ¶
PostCreateNeeded is [Substrate.PostCreateNeeded] asked of surface's family. False for the zero Surface.
func RecordsRemoveUnrowed ¶ added in v0.31.0
RecordsRemoveUnrowed reports whether providerType's family removes a deleted block's objects from the estate's records alone, for a type with no admission-table row as well as one with a row: the record-orphan leg admits an unrowed type through the provider's identity schema on such a pass. True for the GCP family, whose Asset Inventory leg plans no removal of its own (GitHub issue #2134, item 1). Sweeps stays false for it, because the deposed-record and label-list paths that read Sweeps need a sweep that lists, and the Asset search does not list for removal.
The identity package's no-orphan-recovery warning asks both: a type of such a family is recovered from its record once its last block is deleted, which is what the warning says nothing does.
func SurfaceOf ¶
SurfaceOf is the marker surface a resource type of provider providerType carries, read off its schema, or false when it has none.
GitHub issue #1742: the family is chosen by provider, the rule NotACarrier and ForProvider use, and only that family is asked. A schema alone cannot tell two families apart - an azurerm type's tags map is shaped exactly like an AWS type's - so dispatching on it handed one family's types to whichever claimed the shape first. A provider no family claims (azurerm, datadog today) keeps the compatible default the node stamp has always had: every family is asked in All's order and the first surface its schema carries is the one stamped (maintainer ruling on #1742, 2026-10-02).
This is the question live-mv's surface switch and live-import's carrier choice asked.
func Sweeps ¶
Sweeps reports whether providerType names a family whose own sweep leg finds its objects independently of internal/live/identity's admission table (GitHub issue #1581): a type belonging to such a family needs no row there to be found again once its last block is removed.
It is the family's Substrate.SweepFindsUnadmitted (GitHub issue #1742: it used to compare the sweep kind to SweepLabelList, which a third family's own sweep could never answer). Kubernetes qualifies: its leg lists every kind the cluster serves and joins the result against the estate's objects. Azure answers true from its Resource Graph sweep's design ([azure.SweepFindsUnadmitted]); until that leg is built, the plan's NO_SWEEP_LEG gap names the family instead. GCP's Asset Inventory leg sees every labelled asset type but plans no removal and cannot see an unlabelled type, so it answers false and its deleted blocks are the record leg's (GitHub issue #2134). AWS's own sweep (SweepTaggingIndex) is the admission table read a different way, so a type with no row gets nothing extra from it, and neither does an unregistered provider ForProvider does not recognise at all.
This is the question internal/live/identity's no-orphan-recovery warning needs, and it is asked by provider - the resource's own resolved provider configuration, never by a type's schema shape. A type can share a Kubernetes-shaped schema (an object-metadata block, say) with an unrelated provider's type by coincidence; only the provider says which sweep leg, if any, will actually look for it again.
func TombstonesRead ¶ added in v0.22.0
TombstonesRead reports whether a record tombstone written for an object of providerType's family can ever be read: true unless the family is known and its marker dies with the object (Substrate.MarkerOutlivesObject). An unregistered provider answers true, which keeps the tombstone, the conservative side.
Types ¶
type ARMIDPart ¶ added in v0.31.0
type ARMIDPart struct {
Kind string `json:"kind"`
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
Values []string `json:"values,omitempty"`
}
ARMIDPart is one path segment of an ARM id as go-azure-helpers' resourceids declares it, recorded by tools/azurerm-id-gen from the id type's Segments(): Kind "fixed" (a static or resource-provider segment, whose text is Value), "constant" (a constant segment, one of Values) or "value" (a subscription, resource group or user-specified segment, any non-empty text, under Name).
type AWSCreateTagFacts ¶
type AWSCreateTagFacts interface {
CloudControlTypeOrService(tfType string) (string, bool)
TagsAfterCreate(cfnType string) bool
}
AWSCreateTagFacts is the AWS family's entry in Facts (GitHub issue #1708): live/mapping.json's Terraform-to-CloudFormation join and live/registry.json's tagging.tag_on_create. *internal/live/registry.Roster implements it, nil included (every answer false); internal/command puts the embedded roster under AWS's name. An interface so this package stays below the registry.
type AssetSearchSweeper ¶ added in v0.31.0
AssetSearchSweeper is the GCP sweep client: the searcher, and the project the provider block names, searched with every project its resources name.
func (AssetSearchSweeper) SweepKind ¶ added in v0.31.0
func (AssetSearchSweeper) SweepKind() Sweep
type Created ¶
type Created struct {
Addr addrs.AbsResourceInstance
Provider addrs.AbsProviderConfig
// Object is the object ApplyResourceChange returned; cty.NilVal where
// the question is asked before the create.
Object cty.Value
}
Created is the instance a post-create question or write is about: its address, the provider configuration it was applied under, and the object the provider returned. internal/live/projection's CreatedInstance, which the post-create writer is handed, is this type.
type Facts ¶
Facts is what the run knows about types beyond their schemas, keyed by family Substrate.Name and built once per run by the command layer (for AWS, the registry roster: see AWSCreateTagFacts). Each family reads its own entry and asserts it to the shape it owns; an absent entry, a nil one, or one of another shape reads as a family with no facts, which is an ordinary state (a run whose embedded artifacts did not parse).
type Hold ¶
type Hold struct {
// Controller is the controller that holds it: ACK, Crossplane, Helm.
Controller string
// HeldBy names the controller and the object of its that holds this
// one, in one line an operator can act on: "Helm release web/web",
// "ACK s3 controller (s3-v1.0.14), custom resource in namespace team-a".
HeldBy string
}
Hold is a family's answer for a held object.
func ControllerHeld ¶
func ControllerHeld(ev HoldEvidence) (Hold, bool)
ControllerHeld asks every family in All's order whether the object ev was read from is held by a controller, and the first to answer decides. The families read disjoint fields of ev, so the order cannot decide an answer today.
type HoldEvidence ¶
type HoldEvidence struct {
// Tags are an AWS resource's tags.
Tags map[string]string
// Annotations are a Kubernetes object's metadata.annotations.
Annotations map[string]string
// ManagedBy is an Azure Resource Manager object's managedBy property:
// the id of the resource that manages it, empty when none does (#1122).
ManagedBy string
// ARMTags are an Azure Resource Manager object's tags, kept apart from
// Tags so that each family still reads only its own field.
ARMTags map[string]string
// Labels are a GCP resource's labels, and AssetType its Cloud Asset
// Inventory asset type (compute.googleapis.com/Instance): a GCP hold
// is recognised by a reserved label key on a given kind of asset.
Labels map[string]string
AssetType string
}
HoldEvidence is what a sweep leg read off one live object that a family may recognise a controller's hold in. A leg fills what its object carries and leaves the rest empty; each family reads only its own field.
type HoldRecognition ¶
HoldRecognition describes how a family recognises a held object, for the capability matrix (live/substrates.json). Keys empty means the family recognises no controller at all.
type IdentityComponent ¶
type IdentityComponent struct {
Literal string
Attrs []string
Block string
Path []string
OmitIfAbsent bool
SameNameIdentity bool
// Cloud is identity.Component's Cloud, as its string ("subscription-id"
// for the Azure subscription the provider configuration targets).
Cloud string
// ARMTypeSegment is identity.Component's field of the same name.
ARMTypeSegment bool
// ARMSegmentIndex and ARMSegmentAfter are identity.Component's fields
// of the same names: the component is one path segment of the ARM id
// its argument holds (GitHub issue #2206).
ARMSegmentIndex int
ARMSegmentAfter string
// URLEscape is identity.Component's field of the same name.
URLEscape string
// ServerAssignedIfAbsent is identity.Component's field of the same
// name: leaving every one of Attrs unset asks the provider to mint the
// value (GitHub issue #2119; see [markProviderMintedIfAbsent]).
ServerAssignedIfAbsent bool
}
IdentityComponent is the subset of identity.Component a family's synthesis sets. Each field means what the identity package's field of the same name means, except SameNameIdentity, which stands for identity.SameNameIdentity in IdentityAttr.
type IdentitySource ¶ added in v0.31.0
type IdentitySource struct {
// Segment is the segment of the type's own ARM id template this
// attribute is, under the parser's name ("storageAccountName").
Segment string
// Argument: the value is this argument's, read off the applied object.
// It differs from the attribute's own name only where azurerm's
// identity test maps the two explicitly (dns_zone_name is zone_name).
Argument string
// Within: the value is segment ParentSegment of this argument's value,
// an ARM id of the shape ParentTemplate, parsed as azurerm parses it
// ([ARMIDSegment]): ParentSegments is that template segment by segment
// with each one's kind, and ParentInsensitive is whether azurerm's
// validator for the argument parses it insensitively (GitHub issue
// #2140).
Within string
ParentTemplate string
ParentSegment string
ParentSegments []ARMIDPart
ParentInsensitive bool
// Provider: the provider configuration's value, as a cloud value
// string ("subscription-id").
Provider string
// Constant: the same value for every object of the type (a DNS
// record type).
Constant string
}
IdentitySource is where one identity attribute's value comes from. Segment is the type's own id segment the attribute is; exactly one of the rest is set.
type LabelListSweeper ¶
LabelListSweeper is the Kubernetes family's client: the cluster client built from the provider block, whose methods it carries (kubesweep.Sweeper, kubesweep.LabelPatcher).
func (LabelListSweeper) SweepKind ¶
func (LabelListSweeper) SweepKind() Sweep
SweepKind is SweepLabelList.
type ResourceGraphSweeper ¶ added in v0.31.0
ResourceGraphSweeper is the Azure family's client: the Resource Graph client built from one azurerm or azapi provider block, whose methods it carries (azsweep.Searcher, and Subscription, the subscription the block names). internal/command's leg for SweepResourceGraph asks for those capabilities, never for this type.
func (ResourceGraphSweeper) SweepKind ¶ added in v0.31.0
func (ResourceGraphSweeper) SweepKind() Sweep
SweepKind is SweepResourceGraph.
type Substrate ¶
type Substrate interface {
// Name is the family's name. It is one of [Substrate.ProviderTypes],
// and the key a family's entry in [Facts] is filed under.
Name() string
// ProviderTypes are the provider type names [ForProvider] matches to
// this family: ["aws"] and ["kubernetes"], or more than one where a
// family is served by two providers that share its types, such as
// google and google-beta, whose blocks keep the same identity when they
// switch between the two. No type name belongs to two families
// (TestEverySubstrateAnswersEveryQuestion).
ProviderTypes() []string
// Surfaces are the marker surfaces this family's types carry. No
// surface belongs to two families.
Surfaces() []markers.Surface
// SurfaceOf is the surface a resource type's schema carries when it is
// one of this family's, read off the schema and never off the type
// name.
SurfaceOf(block *configschema.Block) (markers.Surface, bool)
// MarkersOf reads the marker map off an object from wherever surface,
// one of this family's, keeps it.
MarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
// Writes is how surface's marker, one of this family's, is written.
Writes(surface markers.Surface) Writes
// CarriesAddress is whether this family's objects carry the block
// address beside tofu-estate, so that the sweep can bind a live object
// back to the block that made it: the AWS tofu-address tag, or the
// Kubernetes address annotation (GitHub issues #1639 to #1641). Where
// the address sits is [Substrate.AddressInMarkers]'s question.
CarriesAddress() bool
// Sweep is which sweep client the family's provider block builds.
Sweep() Sweep
// SweepFindsUnadmitted is whether the family's sweep finds its objects
// independently of internal/live/identity's admission table, drawing
// its universe from the provider and the service rather than from the
// table's rows, so a type with no row is still found once its last
// block is removed ([Sweeps], GitHub issue #1742).
SweepFindsUnadmitted() bool
// MarkerOutlivesObject is whether a deleted object's marker can still
// be listed after the object is gone, which is the only thing a record
// tombstone is for (internal/live/projection's tombstoneFields): AWS's
// tagging index lists a terminated instance's tags for a while. On a
// family whose marker is a field of the object it is false, a tombstone
// has no reader, and a destroy deletes the record instead (GitHub issue
// #1883; live/MARKERS.md's record table).
MarkerOutlivesObject() bool
// Unrecorded is what a declared instance of typeName, one of this
// family's markerless types (no surface at all), means when a record
// store is in play, it holds no record for the instance, and an object
// is live at the identity its configuration computes ([Unrecorded], the
// maintainer's ruling of 2026-10-20). It is per type because whether a
// create at a held identity collides is: enabling a google project
// service that is already enabled succeeds, creating a firewall whose
// name is taken does not. A family answers from its own facts about the
// type, never from a list in a caller.
Unrecorded(typeName string) Unrecorded
// NewSweeper builds the family's estate-sweep client from its provider
// block's evaluated configuration (ok false when the run holds none):
// nil with no error for a family whose sweep runs through the
// configured provider itself ([SweepTaggingIndex]), and the error for a
// block the client cannot be built from. The client is a [Sweeper],
// never a family's concrete type (GitHub issue #1580).
NewSweeper(providerConfig cty.Value, ok bool) (Sweeper, error)
// SynthesizeIdentity is how this family's schema identifies an
// instance of a type the ratified table does not cover, or false when
// the schema is not one of this family's shapes. Asked in [All]'s
// order by internal/live/identity's synthesizeTypeIdentity, and the
// first family to answer decides.
SynthesizeIdentity(typeName string, schema providers.Schema) (SynthesizedIdentity, bool)
// contains filtered or unexported methods
}
Substrate is one provider family's answers. There is one value per family (AWS, Kubernetes), each in its own file, and the package-level functions below dispatch over All. A family answers only for its own surfaces; the dispatch is what answers for a schema or a surface whose family is not yet known.
The split is also what keeps the completeness guard (internal/live/markers/seams_test.go) able to see the callers. The guard credits a caller in another package with the surfaces a callee references itself, and the dispatch functions here reference none: they reach the markers predicates only through the family methods. So a caller that asks SurfaceOf and then acts per surface handles exactly the markers.Surface constants it names, and one that forgets a surface is red.
var AWS Substrate = aws{}
AWS is the hashicorp/aws family: tofu-estate and tofu-address in a settable top-level tags map.
var Azure Substrate = azure{}
Azure is the Azure Resource Manager family (GitHub issue #1120, ruled on #1122 2026-10-19): hashicorp/azurerm and azure/azapi as one family, the AWS marker keys in the tags argument both providers already accept, written lowercase and read case-insensitively (markers.SurfaceARMTags).
Several answers below are facts about Azure's API rather than about this fork's design: [azure.MarkerOutlivesObject], [azure.CreateCollidesOnKey], [azure.SweepFindsUnadmitted] and [azure.PostCreateNeeded]. In this build round they come from Azure's documentation and the two providers' source at the pinned versions (azurerm 5.10.0, azapi 2.13.0), and each says so; they stand until the maintainer measures them on floci-az (live/ADDING-A-PLATFORM.md, requirement 2).
var Google Substrate = google{}
Google is the hashicorp/google family, google and google-beta alike (GitHub issue #1119): the tofu-estate label, an encoded tofu-address label and, on a slotted count member, tofu-slot, all in the top-level labels map, ruled on #1121 (2026-10-11, and the address carrier 2026-10-19).
var Kubernetes Substrate = kubernetes{}
Kubernetes is the hashicorp/kubernetes family: the tofu-estate label, in metadata[0].labels or, for kubernetes_manifest, in manifest.metadata.labels, with the block address in the markers.AddressAnnotation annotation beside it (#1639).
func ForProvider ¶
ForProvider is the family a provider type name belongs to ("aws", "kubernetes"): the one whose Substrate.ProviderTypes lists it. It matches the type name alone, which is what every provider-family check it replaced asked.
type Sweep ¶
type Sweep string
Sweep is which estate-sweep client a family's provider block builds.
const ( // SweepTaggingIndex is the AWS sweep: the discovery legs (the // Resource Groups Tagging API index, the provider's own list // resources, Cloud Control, direct reads), all driven through the // configured provider itself, so no client is built from the block. SweepTaggingIndex Sweep = "tagging-index" // SweepLabelList is the Kubernetes sweep: one label-selected list per // served kind, through a cluster client built from the provider // block's own connection arguments (Substrate.NewSweeper). SweepLabelList Sweep = "label-list" )
const SweepAssetSearch Sweep = "asset-search"
SweepAssetSearch is the GCP sweep: Cloud Asset Inventory's searchAllResources over each project the estate resolves to, through a client built from the provider block's own credentials.
const SweepDataPlane Sweep = "data-plane"
SweepDataPlane is the Azure data-plane listing (GitHub issue #2205): the objects Resource Graph does not index (Key Vault keys, secrets and certificates), listed from the vault's own endpoint. It runs beside SweepResourceGraph, never instead of it.
const SweepResourceGraph Sweep = "resource-graph"
SweepResourceGraph is the Azure sweep (#1122): one Azure Resource Graph query per subscription the estate's resources resolve to, matching the marker case-insensitively.
type Sweeper ¶
type Sweeper interface {
SweepKind() Sweep
}
Sweeper is a family's estate-sweep client as Substrate.NewSweeper builds it from the provider block. SweepKind is the sweep it serves, which must be its family's own Substrate.Sweep: internal/command's leg for that sweep checks it, then asks the client for the capability the leg lists through (for SweepLabelList, kubesweep.Sweeper), never for a family's concrete type, so a third family's client plugs in by naming a sweep and serving its leg (GitHub issues #1580, #1742).
type SynthesizedIdentity ¶
type SynthesizedIdentity struct {
// FromIdentitySchema: the provider's own resource identity schema
// decides, through internal/live/identity's identity-schema route (which
// reads the full schema map and the configuration signal, neither of
// which a family sees). Every other field is empty when it is set,
// except NonAWSProvider and the two below, which say how the family's
// provider answers what that route cannot read from the schemas alone.
FromIdentitySchema bool
// CloudIdentityAttrs, with FromIdentitySchema: identity attributes the
// family's provider fills in from its own provider configuration, by
// name, mapped to the identity package's CloudValue string. The route
// reads such an attribute from that value when the type has no
// argument by the same name, the way an AWS identity schema's
// account_id and region are the provider's (GitHub issue #1120: every
// azurerm 5.10.0 identity schema requires subscription_id, and no
// azurerm argument supplies it). An attribute the type does declare as
// an argument is read from the argument, as before.
//
// A family may set it on an answer without FromIdentitySchema too,
// saying the same of the type's identity schema: internal/live/identity's
// record rung reads it from any answer (GitHub issue #2135), and the
// identity-schema route never sees such an answer.
CloudIdentityAttrs map[string]string
// ARMTemplate, with FromIdentitySchema: the type's ARM id built from
// its own arguments, when the family has one (azurerm's generated
// template, live/azurerm-ids.json). The route renders this as the
// import id where the template supplies exactly the identity schema's
// attributes, so the identity is an ARM id other blocks can collide
// with ([SynthesizedIdentity.ARMResourceID]); and it is the route's
// answer where the identity schema names an attribute no argument
// supplies (a parent's name the type takes as the parent's ARM id).
// Nil otherwise.
ARMTemplate *SynthesizedIdentity
// TemplateWithheld, with FromIdentitySchema: why the family has an ARM
// id template for the type and will not offer it (GitHub issue #1120
// unit 17), naming the segment or the row that fails. The route
// refuses with this reason, in place of its own, wherever it does not
// admit the type itself: a template that cannot be resolved exactly
// from configuration is refused by name, never guessed around. Empty
// otherwise.
TemplateWithheld string
// IdentitySources, with FromIdentitySchema: where a record reads each
// attribute of the type's identity schema from, when the family knows
// a source for every one (GitHub issue #2138, the maintainer's option
// (a)). internal/live/identity's record rung reads an attribute the
// type's block does not declare from here: a parent's name parsed out
// of a parent's ARM id argument, a renamed argument, or a constant.
// Nil when the family names none; then IdentitySourcesProblem says why,
// for the refusal.
IdentitySources map[string]IdentitySource
// IdentitySourcesProblem, with FromIdentitySchema: why IdentitySources
// is nil for a type that serves an identity schema, naming the
// attribute, for strict-markers-unrecordable to say. Empty otherwise.
IdentitySourcesProblem string
// NonAWSProvider is identity.TypeIdentity's field of the same name.
NonAWSProvider bool
// Components, ImportSyntax and IdentityAttrs are the entry's, in
// identity.TypeIdentity's terms.
Components []IdentityComponent
ImportSyntax string
IdentityAttrs []string
// ARMResourceID is identity.TypeIdentity's field of the same name: the
// identity is an Azure Resource Manager resource id (GitHub issue
// #1120), whole and case-insensitive.
ARMResourceID bool
}
SynthesizedIdentity is a family's reading of one type's identity from its schema.
func SynthesizeIdentity ¶ added in v0.22.0
func SynthesizeIdentity(typeName string, schema providers.Schema) (SynthesizedIdentity, bool)
SynthesizeIdentity asks every family in All's order how typeName's schema identifies an instance, and the first to answer decides (internal/live/identity's synthesizeTypeIdentity). The answer is AWS's identity-schema route (SynthesizedIdentity.FromIdentitySchema) for any type no earlier family claims, which is why a family placed after AWS would never be asked (GitHub issue #1742).
type Unrecorded ¶ added in v0.31.0
type Unrecorded string
Unrecorded is a family's answer for a declared instance of a markerless type that has no record in the estate's record store while an object is live at the identity its configuration computes.
The ruling (maintainer, 2026-10-20, "option A"): such an object is not owned. Nothing on it says whose it is, and the record is the only place this estate could have written that it made the object. Somebody else may have, and a destroy that left the object in place (a google service with disable_on_destroy unset) leaves exactly the same shape behind. What follows depends on whether the create the configuration declares would collide with the object, which is the family's to say.
const ( // UnrecordedCreates: not owned, and the plan proposes the create. The // default for a family that claims nothing, and right wherever a create // at a held identity is idempotent or cannot conflict. UnrecordedCreates Unrecorded = "creates" // UnrecordedNameHeld: not owned, and the create would be refused while // the object holds the identity, so the plan stops with the name-held // refusal instead of proposing it. UnrecordedNameHeld Unrecorded = "name-held" // UnrecordedParentOwned: admitted as before. The family's markerless // types are attachments and associations identified by a composite of // tagged parents, so their ownership is their parents' ownership and a // refusal would only ever be a false accusation (internal/live/ // projection's checkOwnership doc comment, live/MARKERS.md). UnrecordedParentOwned Unrecorded = "parent-owned" )
func UnrecordedFor ¶ added in v0.31.0
func UnrecordedFor(providerType, typeName string) Unrecorded
UnrecordedFor is Substrate.Unrecorded for providerType's family, and UnrecordedCreates for a provider no family claims: nothing vouches that its markerless objects are anybody's, and nothing says a create there collides.
type Write ¶
type Write string
Write is how a marker reaches a live object.
const ( // WriteInCreate: the marker rides in the create call's own arguments, // put there by the node-path stamp (internal/live/projection). WriteInCreate Write = "in-create" // WriteTagsPlan: a plan-then-apply through the provider that changes // the tags map and nothing else, refused if it would change anything // more (internal/live/liveimport's tags.go, internal/live/mv's // rewrite.go). WriteTagsPlan Write = "tags-only-plan" // WriteLabelsPlan: the same, confined to metadata[0].labels // ([markers.WithLabels]; liveimport's labels.go, mv's label.go). WriteLabelsPlan Write = "labels-only-plan" // WriteAPIPatch: one merge patch of the label straight to the API // server under the caller's own credential, sent first with // dryRun=All and refused if the answer changes anything beyond the // labels (kubesweep.LabelPatcher; liveimport's manifest.go, ruled on // #1109 and #1104). WriteAPIPatch Write = "api-patch" )
const ( // WriteTaggingAPI: the Resource Groups Tagging API's TagResources, // addressed by the arn the provider returned, issued after the create // of a type whose create call cannot carry tags (live/registry.json's // tag_on_create false, GitHub issue #1084). WriteTaggingAPI Write = "tagging-api" // WriteNeverNeeded: the create call always carries the marker, so no // write follows it. Named rather than left empty so that a family that // has not answered is distinguishable from one that answered "never". WriteNeverNeeded Write = "never-needed" )
const WriteARMTagsAPI Write = "arm-tags-api"
WriteARMTagsAPI is the Azure family's post-create write (#1120 unit 12): ARM's Tags API, a merge of the marker tags onto the object's tag scope (ARMTagScope):
PATCH {resourceManager}{scope}/providers/Microsoft.Resources/tags/default?api-version=2021-04-01
{"operation": "Merge", "properties": {"tags": {...}}}
Merge adds and replaces the named tags and leaves every other tag on the object as it is (Azure REST API, Tags - Update At Scope). It is the call az tag update --operation Merge makes, and the one azurerm itself uses to tag a subscription (tagsClient.CreateOrUpdateAtScope).
const WriteGoogleLabelsPlan Write = "google-labels-plan"
WriteGoogleLabelsPlan: a plan-then-apply through the provider that changes the top-level labels map and nothing else, refused if it would change anything more: WriteTagsPlan's write and refusal, on the map a hashicorp/google type carries its labels in. Its own value rather than WriteLabelsPlan, which is confined to Kubernetes's metadata[0].labels.
func PostCreateWrite ¶ added in v0.21.0
func PostCreateWrite(surface markers.Surface, created Created, facts Facts) (reason string, write Write, needed bool, err error)
PostCreateWrite is the post-create answer for a create of created whose schema carries surface: whether the create withholds the marker ([Substrate.PostCreateNeeded]), the reason, and the write that puts it back (Writes.PostCreate). err is non-nil when needed is true and the family's answers cannot mark the object:
- the surface names no write, or WriteNeverNeeded;
- created.Provider is set and the family's MarkerWriter names a different write than the surface does;
- the family carries the block address (Substrate.CarriesAddress) outside its marker map ([Substrate.AddressInMarkers] false), where the post-create write, which sets the marker map, cannot put it.
The create side asks it with only created.Addr set, as [Substrate.PostCreateNeeded] is asked, so the MarkerWriter check is the write side's. False, with no error, for the zero Surface.
type Writes ¶
type Writes struct {
// Create is a resource this run creates.
Create Write
// Adopt is an existing object a migration (live-import -approve) or a
// move between estates (live-mv -from-estate) marks.
Adopt Write
// PostCreate is a resource this run creates whose create call cannot
// carry the marker, so it is written onto the object once the create
// returns (GitHub issue #1587, [WriteTaggingAPI], [WriteNeverNeeded]).
PostCreate Write
}
Writes is how one surface's marker is written, by occasion.