Documentation
¶
Index ¶
- Constants
- Variables
- func ActivateLinkNetworking(ctx context.Context, ec2Service ec2service.Service, ...) (*provider.LinkUpdateIntermediaryResourcesOutput, error)
- func AppendRoleAccessMapping(mappings map[string]string, roleLinkData *core.MappingNode, ...)
- func CollectIntermediaryChanges(currentLinkData *core.MappingNode, out *provider.LinkChanges, ...) error
- func CollectS3NotificationChanges(changes *provider.LinkChanges, bucketResourceName string, ...)
- func DeployManagedIntermediary(ctx context.Context, resourceService provider.ResourceService, ...) (*state.LinkIntermediaryResourceState, error)
- func DestroyManagedIntermediary(ctx context.Context, resourceService provider.ResourceService, ...) error
- func FindIntermediaryState(linkState *state.LinkState, resourceID string) *state.LinkIntermediaryResourceState
- func InlineAccessPolicyName() string
- func InlineAccessStatementPath(roleResourceName, sid string) string
- func IntermediaryLeafPath(resourceID, leaf string) string
- func IntermediaryLinkData(intermediaries ...DeployedIntermediary) *core.MappingNode
- func IsRoleNotYetPropagatedError(err error) bool
- func ManagedAccessArnPath(roleResourceName, arn string) string
- func ManagedPolicyArnFieldPath(executionRoleName string) string
- func NetworkAccessFieldName(callerResourceName string) string
- func PermissionFieldPath(executionRoleName string) string
- func PutS3Notification(ctx context.Context, s3Service s3service.Service, target S3NotificationTarget, ...) error
- func RemoveLambdaEnvironmentVariables(ctx context.Context, lambdaService lambdaservice.Service, functionARN string, ...) error
- func RemoveS3Notification(ctx context.Context, s3Service s3service.Service, bucket, baseID string) error
- func RoleNameFromARN(roleARN string) string
- func S3KeyFilterParts(resourceInfo *provider.ResourceInfo, annotationPrefix string) (prefix, suffix string)
- func S3NotificationEntryID(baseID, event string) string
- func S3NotificationEvents(resourceInfo *provider.ResourceInfo, annotationPrefix string) []string
- func S3NotificationLinkData(bucketResourceName string, target S3NotificationTarget, ...) (*core.MappingNode, map[string]string)
- func StageNetworkAccessKnownOnDeploy(callerChanges *provider.Changes, changes *provider.LinkChanges)
- func UpdateLambdaEnvironmentVariables(ctx context.Context, lambdaService lambdaservice.Service, functionARN string, ...) error
- func UpdateLambdaVPCConfig(ctx context.Context, lambdaService lambdaservice.Service, functionARN string, ...) error
- type AccessPolicyLimits
- type AccessPolicyPlan
- type AccessPolicySlot
- type AccessPolicySlotKind
- type CallerNetworking
- type DeployedIntermediary
- type DerivedLeaf
- type IntermediaryIdentity
- type LambdaLinkSetupContext
- type LambdaLinkSetupData
- type ManagedIntermediary
- type NetworkingActivation
- type RoleAccessGrant
- type RoleAccessResult
- type S3NotificationSpecEntry
- type S3NotificationTarget
- type StageIntermediary
Constants ¶
const ( // PermissionFieldName is the name of the field in the link data that // contains the permission (statement) object. // This should be used across all link implementations that store permissions // in the link data. PermissionFieldName = "permission" // ManagedPolicyArnFieldName is the name of the field in the link data that holds // the ARN of the allocator's managed policy when a grant overflows from the // role's inline policy into an attached managed policy. ManagedPolicyArnFieldName = "managedPolicyArn" )
const IntermediaryLinkDataKey = "intermediaries"
IntermediaryLinkDataKey is the top-level linkData key under which a link's managed intermediaries are projected.
Variables ¶
var ( // S3LambdaTarget describes a Lambda function notification destination. S3LambdaTarget = S3NotificationTarget{ ConfigField: "lambdaConfigurations", ARNField: "function", } // S3QueueTarget describes an SQS queue notification destination. S3QueueTarget = S3NotificationTarget{ ConfigField: "queueConfigurations", ARNField: "queue", } // S3TopicTarget describes an SNS topic notification destination. S3TopicTarget = S3NotificationTarget{ ConfigField: "topicConfigurations", ARNField: "topic", } )
var ErrAccessPolicyBudgetExhausted = errors.New("execution role permission budget exhausted")
ErrAccessPolicyBudgetExhausted is returned (wrapped) when a grant cannot fit in the role's inline policy or any managed policy slot. Callers detect it with errors.Is and enrich it with the role and link identity for a clear message.
Functions ¶
func ActivateLinkNetworking ¶ added in v0.2.0
func ActivateLinkNetworking( ctx context.Context, ec2Service ec2service.Service, input *provider.LinkUpdateIntermediaryResourcesInput, activation NetworkingActivation, output *provider.LinkUpdateIntermediaryResourcesOutput, ) (*provider.LinkUpdateIntermediaryResourcesOutput, error)
ActivateLinkNetworking opens the networking a VPC-attached caller needs to reach its link target, and ref-count-aware removes it on destroy. It is a no-op when the caller is not attached to a VPC.
The caller must be connected to a flex VPC (often in reference mode) in the same blueprint.
func AppendRoleAccessMapping ¶ added in v0.2.0
func AppendRoleAccessMapping( mappings map[string]string, roleLinkData *core.MappingNode, roleResourceName, linkDataKey, sid string, result RoleAccessResult, )
AppendRoleAccessMapping records, into mappings and the role's link-data node, the attribution for an allocator placement so the role does not treat the grant as drift. Inline placements map the statement (by Sid); managed (overflow) placements map the attached managed policy ARN (and add it to the link data). roleLinkData is the per-role link-data object (the value stored under the role's link-data key) and may be mutated to carry the managed policy ARN.
func CollectIntermediaryChanges ¶ added in v0.2.0
func CollectIntermediaryChanges( currentLinkData *core.MappingNode, out *provider.LinkChanges, intermediary StageIntermediary, ) error
CollectIntermediaryChanges projects a managed intermediary into the given LinkChanges by diffing its linkData entry against the prior linkData. The constant "resourceType" leaf is collected directly; each derived leaf is collected from its source resource spec so the existing helper routes unresolved/computed values to FieldChangesKnownOnDeploy.
func CollectS3NotificationChanges ¶ added in v0.2.0
func CollectS3NotificationChanges( changes *provider.LinkChanges, bucketResourceName string, target S3NotificationTarget, resourceAChanges, resourceBChanges *provider.Changes, )
CollectS3NotificationChanges projects the link's bucket notification contribution as a known-on-deploy change when either linked resource is new (the entries embed the destination ARN, which is resolved on deploy), so the bucket notification change is surfaced in the staged plan alongside the permission intermediary.
func DeployManagedIntermediary ¶ added in v0.2.0
func DeployManagedIntermediary( ctx context.Context, resourceService provider.ResourceService, instanceID, instanceName string, providerCtx provider.Context, priorState *state.LinkIntermediaryResourceState, intermediary ManagedIntermediary, ) (*state.LinkIntermediaryResourceState, error)
DeployManagedIntermediary creates or updates the link-owned resource and returns its persisted intermediary state (carrying the Cloud Control identifier needed for later updates/destroys). A nil priorState yields a create; an existing one yields an update.
func DestroyManagedIntermediary ¶ added in v0.2.0
func DestroyManagedIntermediary( ctx context.Context, resourceService provider.ResourceService, instanceID string, providerCtx provider.Context, priorState *state.LinkIntermediaryResourceState, ) error
DestroyManagedIntermediary removes a previously-deployed link-owned resource. A nil priorState is a no-op (nothing was recorded to remove).
func FindIntermediaryState ¶ added in v0.2.0
func FindIntermediaryState( linkState *state.LinkState, resourceID string, ) *state.LinkIntermediaryResourceState
FindIntermediaryState returns the recorded intermediary state with the given resource ID from the current link state, or nil.
func InlineAccessPolicyName ¶ added in v0.2.0
func InlineAccessPolicyName() string
InlineAccessPolicyName returns the name of the shared inline policy the allocator manages on a role. Links use it to build the ResourceDataMappings path that attributes their statement to the link (suppressing role drift).
func InlineAccessStatementPath ¶ added in v0.2.0
InlineAccessStatementPath returns the ResourceDataMappings key that targets this link's statement (by Sid) within the role's shared inline allocator policy, so the role's drift/deploy attributes the statement to the link instead of stripping it.
func IntermediaryLeafPath ¶ added in v0.2.0
IntermediaryLeafPath returns the linkData path of a single intermediary leaf, e.g. $["intermediaries"]["<resourceID>"]["sourceArn"].
func IntermediaryLinkData ¶ added in v0.2.0
func IntermediaryLinkData(intermediaries ...DeployedIntermediary) *core.MappingNode
IntermediaryLinkData builds the linkData node ({intermediaries: {<id>: {resourceType, ...leaves}}}) a link should return from UpdateIntermediaryResources, so that what is persisted matches the projection diffed at stage time.
func IsRoleNotYetPropagatedError ¶ added in v0.2.0
IsRoleNotYetPropagatedError reports whether an error is the transient validation failure AWS returns when a Lambda event source mapping is created before the execution role's freshly-granted stream/queue read permissions have propagated (IAM eventual consistency).
It anchors on the smithy API error code (InvalidParameterValueException) as well as a message substring so it is decoupled from unrelated errors sharing the same wording. The broader "Cannot access" substring covers the stream ("Cannot access stream ...") and queue variants of the message.
This is an eventual-consistency retry the AWS SDK's default retryer will NOT perform (InvalidParameterValueException is a 400-level validation error, outside the SDK's retryable set). Callers wrap CreateEventSourceMapping with pluginutils.Retryable so the blueprint engine re-runs the idempotent intermediary update, re-asserting the role grant before retrying.
func ManagedAccessArnPath ¶ added in v0.2.0
ManagedAccessArnPath returns the ResourceDataMappings key that targets the allocator's attached managed policy ARN within the role's managedPolicyArns, so the role's drift/deploy attributes the attachment to the link instead of detaching it. Used for managed (overflow) placements.
func ManagedPolicyArnFieldPath ¶ added in v0.2.0
ManagedPolicyArnFieldPath returns the field path for the managed policy ARN in the link data, keyed by the execution role's link-data name.
func NetworkAccessFieldName ¶ added in v0.2.0
NetworkAccessFieldName is the synthetic link-data field name used to signal, in staged changes, that a VPC-attached caller's network access to a link target will be configured at deploy time (a VPC endpoint or a security-group rule opened by ActivateLinkNetworking).
func PermissionFieldPath ¶
PermissionFieldPath returns the field path for a permission (statement) object in the link data, keyed by the execution role's link-data name. This should be used across all link implementations that store permissions in the link data.
func PutS3Notification ¶ added in v0.2.0
func PutS3Notification( ctx context.Context, s3Service s3service.Service, target S3NotificationTarget, bucket, baseID, targetARN string, events []string, prefix, suffix string, ) error
PutS3Notification merges this link's notification entries (one per event, identified by "<baseID>-<eventSlug>") into the bucket's notification configuration for the given destination type, preserving every other entry, and writes it back. The caller must hold the bucket lock.
func RemoveLambdaEnvironmentVariables ¶
func RemoveLambdaEnvironmentVariables( ctx context.Context, lambdaService lambdaservice.Service, functionARN string, currentConfig *types.FunctionConfiguration, envVarsToRemove []string, ) error
RemoveLambdaEnvironmentVariables removes the environment variables for a Lambda function by removing the specified environment variables from the current environment variables. This is mostly useful for links that connect lambda functions to other resources.
func RemoveS3Notification ¶ added in v0.2.0
func RemoveS3Notification( ctx context.Context, s3Service s3service.Service, bucket, baseID string, ) error
RemoveS3Notification drops this link's notification entries (those whose id begins with "<baseID>-") from the bucket's notification configuration across all destination types, preserving every other entry. The caller must hold the bucket resource lock.
func RoleNameFromARN ¶ added in v0.2.0
RoleNameFromARN extracts the IAM role name from a role ARN of the form arn:aws:iam::<account>:role/<roleName> (the role name is the final path segment, so this also handles ARNs that include a path).
func S3KeyFilterParts ¶ added in v0.2.0
func S3KeyFilterParts(resourceInfo *provider.ResourceInfo, annotationPrefix string) (prefix, suffix string)
S3KeyFilterParts reads the prefix/suffix filter annotations (aws.{prefix}.filterPrefix and aws.{prefix}.filterSuffix).
func S3NotificationEntryID ¶ added in v0.2.0
S3NotificationEntryID is the stable per-event S3 notification id this link writes, "<baseID>-<eventSlug>".
func S3NotificationEvents ¶ added in v0.2.0
func S3NotificationEvents(resourceInfo *provider.ResourceInfo, annotationPrefix string) []string
S3NotificationEvents reads the indexed event annotations (aws.{prefix}.event.<index>) from the target resource in order, stopping at the first absent index. When none are set it defaults to s3:ObjectCreated:*.
func S3NotificationLinkData ¶ added in v0.2.0
func S3NotificationLinkData( bucketResourceName string, target S3NotificationTarget, entries []S3NotificationSpecEntry, ) (*core.MappingNode, map[string]string)
S3NotificationLinkData builds the link-data subtree and the resource-data mappings that claim the link's notification entries on the bucket, so the bucket's drift/update does not strip them. Each entry is mapped by its compound (destination ARN AND event) selector onto the bucket's notification-configuration array.
func StageNetworkAccessKnownOnDeploy ¶ added in v0.2.0
func StageNetworkAccessKnownOnDeploy( callerChanges *provider.Changes, changes *provider.LinkChanges, )
StageNetworkAccessKnownOnDeploy appends a known-on-deploy network-access signal to the link changes when the caller (resource A) is detectably attached to a VPC.
Networking activation (interface/gateway VPC endpoints, security-group rules) happens at deploy and is a no-op for callers not attached to a VPC, and its concrete values (endpoint id, rule) are only known at deploy. So this is a best-effort, value-less signal: it fires when the caller's current state or resolved spec already shows a VPC attachment (an explicit vpcConfig, or one drift-mapped from a placement link on a re-deploy), and stays silent otherwise.
func UpdateLambdaEnvironmentVariables ¶
func UpdateLambdaEnvironmentVariables( ctx context.Context, lambdaService lambdaservice.Service, functionARN string, currentConfig *types.FunctionConfiguration, envVarsToSet map[string]string, ) error
UpdateLambdaEnvironmentVariables updates the environment variables for a Lambda function by merging the current environment variables with the new ones. This is mostly useful for links that connect lambda functions to other resources.
func UpdateLambdaVPCConfig ¶ added in v0.2.0
func UpdateLambdaVPCConfig( ctx context.Context, lambdaService lambdaservice.Service, functionARN string, subnetIDs []string, securityGroupIDs []string, ) error
UpdateLambdaVPCConfig sets a Lambda function's VPC configuration (subnets and security groups) via UpdateFunctionConfiguration. Passing empty slices detaches the function from its VPC, which AWS treats as clearing the configuration.
Types ¶
type AccessPolicyLimits ¶ added in v0.2.0
type AccessPolicyLimits struct {
// MaxInlineBytes caps the inline policy document (headroom under the 10,240
// aggregate inline-policy limit per role).
MaxInlineBytes int
// MaxManagedBytes caps each managed policy document (headroom under 6,144).
MaxManagedBytes int
// MaxManagedSlots caps how many managed policies the allocator will attach,
// leaving room under the per-role managed-policy attachment limit (10 default).
MaxManagedSlots int
}
AccessPolicyLimits are the size/count budgets the allocator packs against. The defaults keep headroom under the documented IAM quotas and leave attachment budget for practitioner-managed policies.
func DefaultAccessPolicyLimits ¶ added in v0.2.0
func DefaultAccessPolicyLimits() AccessPolicyLimits
DefaultAccessPolicyLimits returns conservative budgets with headroom under the IAM quotas.
type AccessPolicyPlan ¶ added in v0.2.0
type AccessPolicyPlan struct {
// Upserts are slots whose document must be written (inline put, or managed
// create / new version).
Upserts []AccessPolicySlot
// Deletes are slots that are now empty and must be removed (inline delete, or
// managed detach + delete).
Deletes []AccessPolicySlot
// PlacedSlot is the name of the slot the grant was placed in, recorded in link
// data so later reconciles target it directly. Empty when the grant was removed.
PlacedSlot string
}
AccessPolicyPlan is the set of mutations to bring the role's policies in line with the desired statement set, plus the slot the grant now lives in.
type AccessPolicySlot ¶ added in v0.2.0
type AccessPolicySlot struct {
Kind AccessPolicySlotKind
Name string
ARN string
Statements map[string]json.RawMessage
}
AccessPolicySlot is one Bluelink-managed policy resource and the statements it holds, keyed by Sid. ARN is only set for managed slots that already exist.
type AccessPolicySlotKind ¶ added in v0.2.0
type AccessPolicySlotKind int
AccessPolicySlotKind distinguishes an inline role policy from a managed policy.
const ( AccessPolicyInline AccessPolicySlotKind = iota AccessPolicyManaged )
type CallerNetworking ¶ added in v0.2.0
CallerNetworking is the VPC attachment of a link's caller, which is the compute running the workload, independent of the compute platform. A Lambda function's VpcConfig, an ECS task's awsvpc configuration and an EKS pod's networking all reduce to a VPC, the subnets the workload's network interfaces live in and the security groups attached to them. Keeping the caller platform-agnostic lets the same networking activation serve FaaS and containerised compute (ECS/EKS).
func CallerNetworkingFromLambdaVPCConfig ¶ added in v0.2.0
func CallerNetworkingFromLambdaVPCConfig(vpcConfig *types.VpcConfigResponse) CallerNetworking
CallerNetworkingFromLambdaVPCConfig adapts a Lambda function's VpcConfig into the platform-agnostic CallerNetworking that ActivateLinkNetworking consumes. A nil or unattached config yields a zero CallerNetworking, which the helper treats as a no-op.
type DeployedIntermediary ¶ added in v0.2.0
type DeployedIntermediary struct {
Identity IntermediaryIdentity
Leaves map[string]*core.MappingNode
}
DeployedIntermediary is a resolved intermediary to persist into link data at update time. Leaves excludes "resourceType", which is derived from Identity.
type DerivedLeaf ¶ added in v0.2.0
type DerivedLeaf struct {
// Leaf is the linkData leaf name under the intermediary entry, e.g. "sourceArn".
Leaf string
// ResourceChanges is the linked resource (ResourceAChanges or ResourceBChanges) the
// value is read from.
ResourceChanges *provider.Changes
// ResourceSpecPath is the path of the source value in the resource spec, e.g.
// "$.spec.arn".
ResourceSpecPath string
}
DerivedLeaf is an intermediary linkData leaf whose value comes from a linked resource's spec field (so it may be resolvable now or known on deploy).
type IntermediaryIdentity ¶ added in v0.2.0
IntermediaryIdentity is the deploy-stable identity of a single link-owned intermediary. It is computed identically at stage and update time so the surfaced change matches what is deployed.
type LambdaLinkSetupContext ¶
type LambdaLinkSetupContext struct {
FunctionARN string
LambdaOutput *types.FunctionConfiguration
// The name of the role in AWS.
RoleName string
// The name of the role resource in the blueprint.
RoleResourceName string
// The resource state of the role in the blueprint.
RoleResourceState *state.ResourceState
LambdaService lambdaservice.Service
}
func SetupLinkFromLambdaFunction ¶
func SetupLinkFromLambdaFunction( ctx context.Context, setupData *LambdaLinkSetupData, lambdaService lambdaservice.Service, resourceService provider.ResourceService, providerCtx provider.Context, ) (*LambdaLinkSetupContext, error)
SetupLinkFromLambdaFunction sets up a link from a Lambda function to another resource.
type LambdaLinkSetupData ¶
type LambdaLinkSetupData struct {
LambdaFuncResourceInfo *provider.ResourceInfo
LoadRoleInfo bool
}
type ManagedIntermediary ¶ added in v0.2.0
type ManagedIntermediary struct {
// ResourceType is the Bluelink resource type to deploy, e.g. "aws/lambda/permission".
ResourceType string
// ResourceID is a deterministic, instance-stable identifier for the resource so
// repeated reconciles target the same intermediary.
ResourceID string
// ResourceName is a human-readable logical name for the resource.
ResourceName string
// Spec is the resource's desired spec (free-form fields may be JSON strings; the
// engine parses them at the Cloud Control boundary).
Spec *core.MappingNode
}
ManagedIntermediary describes a single Cloud Control resource a link owns.
type NetworkingActivation ¶ added in v0.2.0
type NetworkingActivation struct {
// Caller is the VPC attachment of the link's caller (resource A), read from its
// computed state and reduced to a platform-agnostic form.
Caller CallerNetworking
// Region is the AWS region the caller and its target are deployed in.
Region string
// AWSService, when set, provisions an interface VPC endpoint so a VPC-isolated
// caller can reach an AWS managed service. It is the short service segment (e.g.
// "lambda", "sns", "sqs", "secretsmanager", "ssm", "kms"); the full endpoint
// service name is com.amazonaws.<region>.<AWSService>.
AWSService string
// EndpointType selects the VPC endpoint type. Interface (the default) provisions an
// interface endpoint with a security group; Gateway provisions a gateway endpoint
// attached to the caller's route tables (used for S3 and DynamoDB).
EndpointType ec2types.VpcEndpointType
// TargetSecurityGroupID and TargetPort, when set, pair the caller's security group
// with an in-VPC resource's security group (RDS proxy/instance, ElastiCache).
TargetSecurityGroupID string
TargetPort int32
}
NetworkingActivation describes the networking a VPC-attached caller needs opened to reach a link target. When the caller is not VPC-attached ActivateLinkNetworking is a no-op.
type RoleAccessGrant ¶ added in v0.2.0
type RoleAccessGrant struct {
// RoleName is the IAM role to grant access on (the resolved execution role).
RoleName string
// SID identifies this link's statement within the role's policies.
SID string
// Statement is the IAM policy statement to upsert; nil removes the grant.
Statement map[string]any
// Limits overrides the packing budgets; the zero value uses the defaults.
Limits AccessPolicyLimits
}
RoleAccessGrant describes a single link's access grant to apply to a role.
type RoleAccessResult ¶ added in v0.2.0
type RoleAccessResult struct {
// PlacedSlot is the name of the policy the grant now lives in (empty on remove).
PlacedSlot string
// PlacedSlotInline reports whether the grant landed in the role's inline policy
// (true) versus an attached managed policy (false). Meaningful only when
// PlacedSlot is non-empty.
PlacedSlotInline bool
// PlacedSlotARN is the ARN of the managed policy the grant landed in, set only
// for managed (overflow) placements. Links use it to build the
// managedPolicyArns ResourceDataMapping that attributes the attachment to the
// link (suppressing role drift).
PlacedSlotARN string
}
RoleAccessResult reports where the grant was placed (empty when removed).
func ReconcileRoleAccessPolicy ¶ added in v0.2.0
func ReconcileRoleAccessPolicy( ctx context.Context, iamService iamservice.Service, grant RoleAccessGrant, ) (RoleAccessResult, error)
ReconcileRoleAccessPolicy applies a single access grant to a role, packing it into the role's inline policy or a managed policy slot per the allocator's strategy. The caller must hold the per-role lock.
type S3NotificationSpecEntry ¶ added in v0.2.0
type S3NotificationSpecEntry struct {
Event string
SpecNode *core.MappingNode
// Selector is the array-item selector (e.g.
// `[@.function = "<arn>" && @.event = "<event>"]`) that targets this entry.
Selector string
}
S3NotificationSpecEntry is one per-event notification entry in the bucket-spec shape, together with the compound selector that uniquely identifies it within the bucket's notification array (by destination ARN AND event).
func S3NotificationSpecEntries ¶ added in v0.2.0
func S3NotificationSpecEntries( target S3NotificationTarget, targetARN string, events []string, prefix, suffix string, ) []S3NotificationSpecEntry
S3NotificationSpecEntries builds the per-event spec-shaped entries for a destination, used both to record the link's contribution in link data and to map it onto the bucket's notification configuration for drift suppression.
type S3NotificationTarget ¶ added in v0.2.0
type S3NotificationTarget struct {
// ConfigField is the notification-configuration array on the bucket spec, e.g.
// "lambdaConfigurations", "queueConfigurations" or "topicConfigurations".
ConfigField string
// ARNField is the spec attribute holding the destination ARN within an entry, e.g.
// "function", "queue" or "topic".
ARNField string
}
S3NotificationTarget identifies the destination type of a bucket notification and the spec/SDK field that holds the destination ARN.
type StageIntermediary ¶ added in v0.2.0
type StageIntermediary struct {
Identity IntermediaryIdentity
DerivedLeaves []DerivedLeaf
}
StageIntermediary describes one link-owned intermediary to project into link changes at stage time.