Skip to main content

Effective-Access Export

The effective-access export lets you download a complete, point-in-time snapshot of which principals can perform which actions on which resources for a given identity-provider connection. The export is produced automatically on every sync for opted-in AWS connections.

Enabling the export

Enable the export per-connection via the PATCH endpoint. Only AWS Account and AWS Organization connections are supported in v1.

PATCH /nhi/connections/{connection_id}
SlashID-OrgID: <your-org-id>
Content-Type: application/json

{
"source": "aws_account",
"access_export_enabled": true
}

The flag is persisted in the connection record. On the next successful sync the scanner writes the export object to GCS, and all subsequent syncs update it.

Retrieving the download URL

GET /nhi/connections/{connection_id}/access-export/download-url
SlashID-OrgID: <your-org-id>

A successful response (200 OK) contains:

{
"result": {
"url": "https://storage.googleapis.com/...",
"expires_at": "2024-01-15T10:30:00Z"
}
}
FieldDescription
urlV4 pre-signed GCS URL, valid for 15 minutes. Download immediately and do not cache.
expires_atUTC timestamp after which the URL is no longer accepted by GCS.

A 404 response means no export has been produced yet for this connection. Enable the flag and wait for the next sync to complete.

Decoding the export

The download is a zstd-compressed stream of length-delimited protobuf records (using the protodelim framing convention).

The canonical schema is published at:

https://cdn.slashid.com/schemas/effective-access-export/v1.proto

Decoding outline (Go):

import (
"bufio"
"github.com/klauspost/compress/zstd"
"google.golang.org/protobuf/encoding/protodelim"
)

decoder, _ := zstd.NewReader(httpBody)
defer decoder.Close()
br := bufio.NewReader(decoder) // protodelim.UnmarshalFrom requires io.ByteReader

for {
var rec accessexport.Record // generated from v1.proto
if err := protodelim.UnmarshalFrom(br, &rec); err == io.EOF {
break
}
// handle rec
}

Record ordering

The stream begins with a single Header, followed by all entity records (Resource and Principal, in no guaranteed sub-order relative to each other), followed by all Relationship records.

Because every entity precedes every relationship, a consumer should first index every entity by its common.handle, then process the relationships and resolve each from/to reference against that index.

Record types

Shared entity fields (EntityCommon)

Both Resource and Principal embed an EntityCommon message under the common field. These two identifiers are easy to confuse, so note the distinction:

FieldDescription
handleA uint64 export-local reference. It is ephemeral — valid only within this one export — and is what Relationship.from/to point at. Index entities by common.handle to resolve relationships. Not a business key.
entity_idThe stable, global SlashID identifier (a sha256 hex string). Use this to deduplicate or join the entity to records in your own store across exports.
source_typeThe provider/source, e.g. aws_account.
typeThe entity type, e.g. s3_bucket, aws_iam_user.
identifierSource-native identifier (e.g. an ARN).
source_identifierThe containing scope (the AWS account / project ID).
display_nameBest human-readable label.

Resource

Represents an AWS resource. In v1 the export emits only S3 buckets (type = s3_bucket; check the Header's included_resource_types); the schema is generic and may carry additional resource types in future versions. Carries only the shared common (EntityCommon) fields above; for a resource, source_identifier is the AWS account ID that owns it.

Principal

Represents a human or non-human identity that has access (IAM user, role, federated identity, etc.). Carries the shared common (EntityCommon) fields plus:

FieldDescription
emailEmail address, resolved via the canonical identity. May be empty (e.g. for roles and service accounts). When empty, fall back to common.identifier or common.display_name.
canonical_identity_idGroups principals that resolve to one human; may be empty.
is_externaltrue when the principal belongs to a different AWS account than the resource's owner.

Relationship

Represents a resolved effective-access grant: one principal can perform a set of actions on one resource.

FieldDescription
fromA NodeRef pointing at the principal.
toA NodeRef pointing at the resource.
typeA RelationshipType; in v1 always RELATIONSHIP_TYPE_CAN_ACCESS.
granted_viaA repeated list of RelationshipKind values describing how the access was derived. See below.
actionsList of literal IAM action strings (e.g. s3:GetObject), already deny-subtracted.

A NodeRef is { handle (uint64), kind (NodeKind) }. To resolve a relationship, take from.handle / to.handle and look up the entity whose common.handle matches the value (using the index you built from the entity records).

NodeRef.kind (NodeKind)

ValueMeaning
NODE_KIND_UNSPECIFIEDDefault / absent. Treat identically to NODE_KIND_REAL — real entities omit kind to save bytes. Resolve the handle to a Resource/Principal record.
NODE_KIND_REALA real entity. Resolve the handle to a Resource/Principal record.
NODE_KIND_VIRTUALAn opaque compression node with no entity record. Do not attempt to resolve it to an entity; treat it as an intermediate node to traverse. Virtual nodes never appear in the Resource/Principal records.

granted_via values (RelationshipKind)

ValueMeaning
RELATIONSHIP_KIND_DIRECT_POLICYGranted by the principal's own attached (identity-based) policy.
RELATIONSHIP_KIND_GROUP_MEMBERSHIPAccess flows through a group the principal belongs to.
RELATIONSHIP_KIND_ROLEAccess is obtained via a role / assume-role.
RELATIONSHIP_KIND_RESOURCE_POLICYGranted by a resource-based policy.

Mapping to your permission taxonomy

The actions list contains raw AWS IAM action strings. To map them to your own permission levels, enumerate the actions and categorize them — for example, treating s3:GetObject as read-only and s3:DeleteObject as destructive.

v1 fidelity limitations

The export is an over-approximation: it reflects what AWS IAM policies permit in isolation, without accounting for the full enforcement context.

  • Service Control Policies (SCPs) and Organization Policies are evaluated as restrictions only — they can reduce the effective action set but cannot grant access by themselves. Cross-account paths that require both a trust policy and an SCP are modeled conservatively.
  • Permission boundaries are not modeled. A principal that has a permission boundary applied may appear to have more access in the export than it does at runtime.
  • Deny subtraction is literal-token only. An explicit Deny on s3:PutObject will remove that action, but it will not shrink an allowed wildcard such as s3:*. Wildcard action expansion is future work.
  • email may be empty for service accounts and roles. Always fall back to identifier or display_name before rendering.
  • relationship_count in the header may be 0. Do not rely on it to pre-allocate; stream the records instead.
  • The export reflects the state at the time of the most recent successful sync, not real-time IAM state.