reference ~15 min
Context Access API

- [Overview](#overview)

Table of Contents

Overview

ContextAccess is the provider-neutral authority layer for ContextBank resources. It protects context windows, todo lists, and the active banked context through an execution-local scope. The stored ContextWindow and TodoList payloads remain unchanged. Authorization identity is stored separately in versioned metadata sidecars. See Resource Enrollment.

The implementation is in src/main/kotlin/Context/ContextAccess.kt:26-718, and ContextBank enforcement is in src/main/kotlin/Context/ContextBank.kt:155-833.

With no active scope, direct ContextBank calls retain their historical behavior. A scope changes the behavior of calls made inside that scope only. Nested scopes are narrowed by intersection. Coroutine scopes follow dispatcher changes through the coroutine context.

Resource Enrollment

Enroll a resource by associating its storage key with ContextResourceMetadata. The storage key remains an address. The opaque resourceId and optional boundaryId determine authorization matches.

ContextBank.registerResourceMetadata(
    "private-page",
    ContextResourceMetadata(
        resourceKind = ContextResourceKind.CONTEXT_WINDOW,
        resourceId = "resource-17",
        boundaryId = "workspace-3"
    )
)

registerResourceMetadata and registerResourceMetadataSuspend are trusted-host operations. They must run outside an active ContextAccess scope. Removing metadata with deleteResourceMetadata or deleteResourceMetadataSuspend returns the resource to legacy-shared behavior. See src/main/kotlin/Context/ContextBank.kt:2490-2647.

Local metadata is stored beside the payload:

Resource kindPayload pathMetadata path
CONTEXT_WINDOW<lorebook-directory>/<key>.bank<lorebook-directory>/<key>.bank.access
TODO_LIST<todo-list-directory>/<key>.todo<todo-list-directory>/<key>.todo.access
BANKED_CONTEXT<lorebook-directory>/__banked_context.bank<lorebook-directory>/__banked_context.bank.access

The sidecar schema currently uses schemaVersion = 1. Strict decoding requires schemaVersion, resourceKind, and resourceId; unknown schema versions, malformed records, and kind mismatches fail closed. See src/main/kotlin/Context/ContextResourceMetadata.kt:18-180.

Authorization Semantics

Enforcement modes

ContextAccess.issueRootScope has two overloads:

Root-scope callModeMissing metadata
issueRootScope(principal, rights)LEGACY_COMPATIBLEThe resource keeps historical shared access behavior.
issueRootScope(principal, rights, REQUIRE_ENROLLMENT)REQUIRE_ENROLLMENTThe secured operation is denied with METADATA_UNAVAILABLE.

A nested scope uses REQUIRE_ENROLLMENT when either the ambient or requested scope uses that mode. This prevents a nested scope from weakening enrollment requirements. See src/main/kotlin/Context/ContextAccess.kt:264-301 and src/main/kotlin/Context/ContextAccess.kt:678-699.

Selectors and boundaries

A scope grants selectors for each operation independently:

OperationRights propertyMeaning
READreadableResourcesRetrieve a protected resource.
WRITEwritableResourcesUpdate an existing resource.
CREATEcreatableResourcesCreate and enroll a new resource.
DELETEdeletableResourcesDelete a resource or perform a delete-equivalent operation.
ENUMERATEenumerableResourcesList keys visible to the execution.
DELEGATEdelegableIssue an attenuated child scope.

A Resource("resource-17") selector matches only metadata with that exact resourceId. A Boundary("workspace-3") selector matches metadata with that exact boundaryId. Page keys do not match selectors. See src/main/kotlin/Context/ContextAccess.kt:56-109 and src/main/kotlin/Context/ContextAccess.kt:181-217.

Scope attenuation

ContextAccess.attenuate and ContextAccessScope.attenuate intersect every selector set and the delegable flag. They preserve the original principal and enforcement mode. A caller cannot broaden a scope through attenuation.

attenuateForResources also permits a requested exact resource when a trusted resolver confirms that the resource belongs to an ambient boundary. Exact selector equality remains valid. Unknown resources and resolver failures are omitted. Cancellation is propagated. See src/main/kotlin/Context/ContextAccess.kt:303-342 and src/main/kotlin/Context/ContextAccess.kt:598-675.

Denial contract

A protected direct ContextBank operation throws ContextAccessDeniedException before the protected value, retrieval callback, write-back callback, or persistence backend is invoked. The exception exposes:

PropertyTypeMeaning
operationContextAccessOperationOperation that failed.
resourceKindContextResourceKindResource type involved.
resourceKeyStringContextBank storage key used for diagnostics.
reasonContextAccessDenialReasonMISSING_PERMISSION, METADATA_UNAVAILABLE, INVALID_METADATA, or UNSAFE_REFERENCE.

The exception is a SecurityException. See src/main/kotlin/Context/ContextAccess.kt:220-249.

Data Types

ExecutionPrincipal

data class ExecutionPrincipal(val id: String)

id is an opaque host-defined identity. A blank identifier throws IllegalArgumentException. TPipe does not assign provider, workspace, session, or user semantics to this value. See src/main/kotlin/Context/ContextAccess.kt:9-24.

Resource kinds and operations

enum class ContextResourceKind {
    CONTEXT_WINDOW,
    TODO_LIST,
    BANKED_CONTEXT
}

enum class ContextAccessOperation {
    READ,
    WRITE,
    CREATE,
    DELETE,
    ENUMERATE,
    DELEGATE
}

enum class ContextAccessEnforcementMode {
    LEGACY_COMPATIBLE,
    REQUIRE_ENROLLMENT
}

ContextResourceKind identifies the protected value. ContextAccessOperation identifies the requested authority. ContextAccessEnforcementMode controls how an active scope treats a missing metadata sidecar.

Selectors and containment

sealed interface ContextResourceSelector {
    data class Resource(val id: String) : ContextResourceSelector
    data class Boundary(val id: String) : ContextResourceSelector
}

fun interface ContextResourceContainmentResolver {
    suspend fun isResourceInBoundary(
        resource: ContextResourceSelector.Resource,
        boundary: ContextResourceSelector.Boundary
    ): Boolean
}

Selector identifiers must be non-blank. A containment resolver returns membership without returning the protected resource value. Use it only with trusted metadata access. See src/main/kotlin/Context/ContextAccess.kt:56-109.

ContextAccessRights

data class ContextAccessRights(
    val readableResources: Set<ContextResourceSelector> = emptySet(),
    val writableResources: Set<ContextResourceSelector> = emptySet(),
    val creatableResources: Set<ContextResourceSelector> = emptySet(),
    val deletableResources: Set<ContextResourceSelector> = emptySet(),
    val enumerableResources: Set<ContextResourceSelector> = emptySet(),
    val delegable: Boolean = false
)

An empty selector set grants no authority for that operation. delegable controls whether the scope may create a child scope. See src/main/kotlin/Context/ContextAccess.kt:111-129.

ContextAccessScope

ContextAccessScope is created by ContextAccess.issueRootScope and is narrowed through attenuate or attenuateForResources. Its public properties are principal and enforcementMode. The rights snapshot is internal.

fun attenuate(requested: ContextAccessRights): ContextAccessScope

suspend fun attenuateForResources(
    requested: ContextAccessRights,
    resolver: ContextResourceContainmentResolver
): ContextAccessScope

See src/main/kotlin/Context/ContextAccess.kt:131-217.

ContextAccess Functions

Root scope creation

fun issueRootScope(
    principal: ExecutionPrincipal,
    rights: ContextAccessRights
): ContextAccessScope

fun issueRootScope(
    principal: ExecutionPrincipal,
    rights: ContextAccessRights,
    enforcementMode: ContextAccessEnforcementMode
): ContextAccessScope

Create root scopes in trusted host code. The two-argument overload uses LEGACY_COMPATIBLE. Select REQUIRE_ENROLLMENT when every secured operation must have valid metadata. See src/main/kotlin/Context/ContextAccess.kt:264-301.

Scope installation

fun <T> withScope(
    scope: ContextAccessScope,
    block: () -> T
): T

suspend fun <T> withCoroutineScope(
    scope: ContextAccessScope,
    block: suspend () -> T
): T

suspend fun <T> withCurrentScope(
    block: suspend () -> T
): T

withScope uses a thread-local for synchronous code. withCoroutineScope installs a coroutine context element and restores the previous scope after the block. When a scope is already active, the effective principal remains the ambient principal, rights are intersected, and the stricter enforcement mode wins. withCurrentScope preserves the current scope across an explicit coroutine boundary and executes directly when no scope is active. See src/main/kotlin/Context/ContextAccess.kt:387-449.

Delegated child scopes

fun <T> withChildScope(
    principal: ExecutionPrincipal,
    requested: ContextAccessRights,
    block: () -> T
): T

suspend fun <T> withChildCoroutineScope(
    principal: ExecutionPrincipal,
    requested: ContextAccessRights,
    block: suspend () -> T
): T

suspend fun <T> withChildCoroutineScopeForResources(
    principal: ExecutionPrincipal,
    requested: ContextAccessRights,
    resolver: ContextResourceContainmentResolver,
    block: suspend () -> T
): T

A child scope requires an active ambient scope with delegable = true. The child receives the requested principal and an intersection of ambient and requested rights. The ambient enforcement mode remains active. Without an ambient delegable scope, these functions throw ContextAccessDeniedException with operation = DELEGATE, resourceKind = BANKED_CONTEXT, resourceKey = "<execution>", and reason = MISSING_PERMISSION. See src/main/kotlin/Context/ContextAccess.kt:344-538.

Scope inspection

fun currentScope(): ContextAccessScope?

currentScope returns the active scope or null in legacy mode. runBlockingWithCurrentScope is an internal compatibility helper used by synchronous ContextBank methods. See src/main/kotlin/Context/ContextAccess.kt:540-559.

ContextBank Integration

Metadata administration

FunctionResultContract
registerResourceMetadata(key, metadata)UnitTrusted synchronous registration.
registerResourceMetadataSuspend(key, metadata)UnitTrusted suspending registration.
deleteResourceMetadata(key, kind)Booleantrue when a sidecar was deleted.
deleteResourceMetadataSuspend(key, kind)BooleanSuspend-safe equivalent.
getResourceMetadataSuspend(key, kind)ContextResourceMetadata?Returns metadata without exposing the protected value.

Metadata administration requires no active scope. A remote storage mode requires a ContextResourceMetadataBackend; otherwise registration and deletion fail with IllegalStateException. See src/main/kotlin/Context/ContextBank.kt:2490-2589.

Enrolled resource creation

Use the creation functions when creating a resource that must be enrolled atomically with its metadata:

fun createContextWindow(
    key: String,
    metadata: ContextResourceMetadata,
    window: ContextWindow,
    mode: StorageMode = StorageMode.MEMORY_ONLY,
    skipRemote: Boolean = false
)

suspend fun createContextWindowSuspend(
    key: String,
    metadata: ContextResourceMetadata,
    window: ContextWindow,
    mode: StorageMode = StorageMode.MEMORY_ONLY,
    skipRemote: Boolean = false
)

fun createTodoList(
    key: String,
    metadata: ContextResourceMetadata,
    todoList: TodoList,
    mode: StorageMode = StorageMode.MEMORY_ONLY,
    skipRemote: Boolean = false
)

suspend fun createTodoListSuspend(
    key: String,
    metadata: ContextResourceMetadata,
    todoList: TodoList,
    mode: StorageMode = StorageMode.MEMORY_ONLY,
    skipRemote: Boolean = false
)

Creation validates the resource kind, non-blank key, and current metadata schema. It rejects an existing payload or sidecar. Inside a scope, the caller must have CREATE authority for the proposed metadata. A scoped caller receives a defensive copy of the supplied mutable payload before storage. Remote creation requires the metadata backend capability and removes the metadata sidecar if payload persistence fails. See src/main/kotlin/Context/ContextBank.kt:1384-1480 and src/main/kotlin/Context/ContextBank.kt:2894-2986.

Protected direct operations

The following ContextBank paths consult the active scope before accessing the underlying value or backend:

OperationRepresentative APIsEnforcement
ReadgetContextFromBank*, getPagedTodoList*, banked-context readsRequires READ for enrolled metadata.
Writeemplace*, mutations, updateBankedContext*, swapBank*Existing resources require WRITE; creation requires CREATE.
Deletedelete and eviction pathsRequires DELETE.
EnumerationgetPageKeys*, todo-key listing, banked-key listingRequires ENUMERATE; returned keys are filtered to visible resources.
Retrieval/write-backretrieval functions, write-back functions, remote persistenceAuthorization occurs before the callback or backend call.

Legacy MemoryIntrospectionTools methods retain safe-result contracts. Secure callers can use SecureMemoryIntrospectionTools to receive typed denials. See Secure Introspection Tools and src/main/kotlin/Context/MemoryIntrospectionTools.kt:450-476.

Enumeration and resource presence

Enumeration filters keys after checking resource metadata. In LEGACY_COMPATIBLE mode, resources without a sidecar remain visible. In REQUIRE_ENROLLMENT mode, a missing sidecar is denied or excluded. Remote enumeration checks the metadata capability before requesting remote keys. Resource-presence checks use metadata-plane operations and do not retrieve protected values. See src/main/kotlin/Context/ContextBank.kt:713-834.

Mutable reference protection

When a scope is active, APIs that would retain a mutable ContextWindow reference are denied with reason = UNSAFE_REFERENCE:

  • getContextFromBank(key, copy = false) and suspend or mutex variants.
  • getPagedTodoList(key, copy = false) and suspend or mutex variants.
  • getBankedContextWindowSuspend(copy = false).
  • getBankedContextWindowReference().
  • withContextWindowReferenceSuspend(...).

Copy-returning reads remain available. See src/main/kotlin/Context/ContextBank.kt:1191-1220, src/main/kotlin/Context/ContextBank.kt:2186-2262, and src/main/kotlin/Context/ContextBank.kt:2678-2693.

Secure Introspection Tools

SecureMemoryIntrospectionTools provides PCP-facing functions with explicit secure_ registrations and typed ContextAccessDeniedException failures. The original MemoryIntrospectionTools names remain source- and contract-compatible. See src/main/kotlin/Context/SecureMemoryIntrospectionTools.kt:9-333.

Secure tool functions

FunctionParametersReturn
listPageKeys()noneList<String>
getLorebookEntry(pageKey, key)page key, lorebook keyLoreBook?
getLorebook(pageKey)page keyMap<String, LoreBook>
queryLorebook(pageKey, query, minWeight, requiredKeys, aliasKeys, extractRegex)page key and query filtersList<LoreBookQueryResult>
simulateLorebookTrigger(pageKey, text)page key, input textList<String>
searchMemory(pageKey, query, extractRegex)page key, search text, optional regexMemorySearchResult
updateLorebookEntry(pageKey, entry)page key, replacement entryBoolean
deleteLorebookEntry(pageKey, key)page key, lorebook keyBoolean
getTodoList(pageKey)todo-list keyTodoList?
updateTodoList(pageKey, todoList)todo-list key, replacement listBoolean

Read functions perform READ checks. Lorebook and todo-list updates perform WRITE checks. listPageKeys performs ENUMERATE and filters out pages hidden by MemoryIntrospection or ContextLock. See src/main/kotlin/Context/SecureMemoryIntrospectionTools.kt:19-180.

PCP registration

SecureMemoryIntrospectionTools.registerAndEnable(pcpContext)

The function registers these PCP names with FunctionRegistry:

secure_listPageKeys
secure_getLorebookEntry
secure_getLorebook
secure_queryLorebook
secure_simulateLorebookTrigger
secure_searchMemory
secure_updateLorebookEntry
secure_deleteLorebookEntry
secure_getTodoList
secure_updateTodoList

It adds a PCP option only when the context does not already define that function name. See src/main/kotlin/Context/SecureMemoryIntrospectionTools.kt:182-333.

Remote Metadata Backend

Backend capability

ContextResourceMetadataBackend is an optional capability separate from the provider-neutral persistence contract. A backend can implement this capability without changing ContextPersistenceBackend.

interface ContextResourceMetadataBackend {
    suspend fun getResourceMetadata(
        kind: ContextResourceKind,
        key: String
    ): ContextResourceMetadata?

    suspend fun putResourceMetadata(
        kind: ContextResourceKind,
        key: String,
        metadata: ContextResourceMetadata
    )

    suspend fun deleteResourceMetadata(
        kind: ContextResourceKind,
        key: String
    ): Boolean

    suspend fun resourceExists(
        kind: ContextResourceKind,
        key: String
    ): Boolean?
}

The interface is defined in src/main/kotlin/Context/Persistence/ContextResourceMetadataBackend.kt:6-54. A backend returns null for missing metadata and may return null from resourceExists when payload presence cannot be determined. Remote secured creation and enumeration require this capability.

HTTP endpoints

MemoryServer.configureMemoryRouting exposes these endpoints under /context. When P2PRegistry.globalAuthMechanism is configured, the /context interceptor checks the Authorization header before dispatching any endpoint. Unauthorized requests return HTTP 401 with MemoryErrorType.auth. See src/main/kotlin/Context/MemoryServer.kt:15-45.

The {kind} path value is parsed case-insensitively as ContextResourceKind.

GET /context/metadata/{kind}/{key}

Returns the enrolled metadata document. A missing sidecar returns HTTP 404 with MemoryErrorType.notFound. The local metadata reader rejects malformed, unsupported, or kind-mismatched sidecars with ContextAccessDeniedException and reason = INVALID_METADATA.

Example response:

{
  "resourceKind": "CONTEXT_WINDOW",
  "resourceId": "resource-17",
  "boundaryId": "workspace-3",
  "schemaVersion": 1
}

POST /context/metadata/{kind}/{key}

Stores or replaces metadata. The request body is a serialized ContextResourceMetadata. The body resourceKind must match {kind}. Success returns HTTP 200 and the stored metadata. A malformed body or kind mismatch returns HTTP 400 with MemoryErrorType.serialization or MemoryErrorType.badRequest.

Example request body:

{
  "resourceKind": "CONTEXT_WINDOW",
  "resourceId": "resource-17",
  "boundaryId": "workspace-3",
  "schemaVersion": 1
}

DELETE /context/metadata/{kind}/{key}

Deletes the metadata sidecar. Success returns HTTP 204. A missing sidecar returns HTTP 404 with MemoryErrorType.notFound.

GET /context/metadata/{kind}/{key}/exists

Checks payload presence without retrieving the payload. Success returns HTTP 200 with a JSON boolean:

true

Metadata-route bad requests return HTTP 400 with MemoryErrorType.badRequest. See src/main/kotlin/Context/MemoryServer.kt:294-398.

MemoryClient methods

MethodReturnEndpoint
getResourceMetadata(kind, key)MemoryOperationResult<ContextResourceMetadata>GET /context/metadata/{kind}/{key}
putResourceMetadata(kind, key, metadata)MemoryOperationResult<Unit>POST /context/metadata/{kind}/{key}
deleteResourceMetadata(kind, key)MemoryOperationResult<Unit>DELETE /context/metadata/{kind}/{key}
resourceExists(kind, key)MemoryOperationResult<Boolean>GET /context/metadata/{kind}/{key}/exists

MemoryOperationResult.Success contains the parsed value. MemoryOperationResult.Failure contains the HTTP status and MemoryErrorResponse. requireValue and requireSuccess convert failures to MemoryRemoteException. See src/main/kotlin/Context/MemoryClient.kt:293-380 and src/main/kotlin/Context/MemoryTypes.kt:34-103.

Examples

Create and read an enrolled context window

Partial example. Requires an unused key and a configured TPipe storage directory.

suspend fun readPrivatePage(): List<String>
{
    val metadata = ContextResourceMetadata(
        resourceKind = ContextResourceKind.CONTEXT_WINDOW,
        resourceId = "resource-17",
        boundaryId = "workspace-3"
    )
    val rights = ContextAccessRights(
        readableResources = setOf(ContextResourceSelector.Resource("resource-17")),
        creatableResources = setOf(ContextResourceSelector.Resource("resource-17"))
    )
    val scope = ContextAccess.issueRootScope(
        ExecutionPrincipal("agent-1"),
        rights,
        ContextAccessEnforcementMode.REQUIRE_ENROLLMENT
    )

    return ContextAccess.withCoroutineScope(scope) {
        val window = ContextWindow().apply { addText("private value") }
        ContextBank.createContextWindowSuspend("private-page", metadata, window)
        ContextBank.getContextFromBankSuspend("private-page").contextElements
    }
}

Expected output:

[private value]

The creation call requires CREATE. The subsequent read requires READ. The stored payload is copied while a scope is active. Source: src/main/kotlin/Context/ContextBank.kt:1426-1430.

Delegate a contained resource

Partial example. Requires an ambient root scope with the boundary selector and delegable = true, plus a trusted containment resolver.

suspend fun readDelegatedPage(
    resolver: ContextResourceContainmentResolver
): List<String>
{
    val root = ContextAccess.issueRootScope(
        ExecutionPrincipal("manager-1"),
        ContextAccessRights(
            readableResources = setOf(ContextResourceSelector.Boundary("workspace-3")),
            delegable = true
        ),
        ContextAccessEnforcementMode.REQUIRE_ENROLLMENT
    )

    return ContextAccess.withCoroutineScope(root) {
        ContextAccess.withChildCoroutineScopeForResources(
            ExecutionPrincipal("worker-1"),
            ContextAccessRights(
                readableResources = setOf(ContextResourceSelector.Resource("resource-17"))
            ),
            resolver
        ) {
            ContextBank.getContextFromBankSuspend("private-page").contextElements
        }
    }
}

Expected output when the resolver confirms containment:

[private value]

When the resolver returns false or throws a non-cancellation exception, the requested resource selector is omitted and the read is denied with MISSING_PERMISSION. Source: src/main/kotlin/Context/ContextAccess.kt:503-538 and src/main/kotlin/Context/ContextAccess.kt:650-675.

Register secure PCP tools

Partial example. Requires a PcpContext instance.

SecureMemoryIntrospectionTools.registerAndEnable(pcpContext)

Expected result: the ten secure_ function names listed in PCP registration are available through the function registry and PCP options. A denied protected-resource call raises ContextAccessDeniedException; the legacy tool names retain their historical safe-result behavior.

Error Reference

ErrorConditionResolution
ContextAccessDeniedException with MISSING_PERMISSIONThe active scope lacks the operation or selector.Grant the required operation to the intended resource, or attenuate the request to an authorized selector.
ContextAccessDeniedException with METADATA_UNAVAILABLEStrict enrollment requires metadata, or a remote backend lacks metadata capability.Enroll the resource or install a ContextResourceMetadataBackend.
ContextAccessDeniedException with INVALID_METADATAA sidecar is malformed, unsupported, or mismatched with the requested kind.Replace the sidecar with schema version 1 metadata.
ContextAccessDeniedException with UNSAFE_REFERENCEA secured caller requested a retained mutable reference.Use a copy-returning API or perform a scoped mutation through the supported API.
IllegalStateExceptionCreation targets an existing payload or sidecar, or trusted metadata administration is attempted inside a scope.Use a new key, or perform metadata administration outside ContextAccess.
MemoryRemoteExceptionA MemoryClient operation returns a typed remote-memory failure and the caller invokes requireValue or requireSuccess.Inspect failure.statusCode and failure.error, then correct authentication, metadata, payload, or transport state.

See Also