Learning objectives
By the end of this module, you will be able to:
- Keep bundle acquisition, cryptographic verification, policy enforcement, and model invocation as separate gates
- Fail closed when required VCP evidence is absent or invalid
- Format a verified bundle for insertion into any provider's system prompt (the SDK calls this injection)
- Record privacy-aware verification evidence
4.1 — The trust boundary
Bundle bytes arrive as untrusted input and become trusted only once they pass verification. That crossing point is the trust boundary, and a VCP-enabled chat application moves through it in a fixed sequence:
Load bytes
→ validate schema and limits
→ verify hash, signatures, trust, time, revocation, budget, and scope
→ apply local enforcement policy
→ format verified constitutional content
→ call the selected model provider
→ retain a privacy-aware audit record Do not send unverified bundle content to a model. Parsing and formatting are not substitutes for verification.
4.2 — Project setup
python -m venv .venv
. .venv/bin/activate
python -m pip install value-context-protocol==4.2.0
# Or, from the root of a VCP-SDK checkout at the published commit:
python -m pip install ./python Record the exact version or checkout commit before using it as deployment evidence.
VCP works with any model provider and ships no provider credentials, so install your provider's SDK separately and supply its keys yourself.
4.3 — Load trust and bundle data
Your application must define where bundles come from, and where its trust anchors (the issuer and auditor public keys you have decided to trust) come from. Keep the adapters that fetch them narrow and reviewable:
bundle = bundle_repository.load("family.safe.guide", "1.2.0")
trust = trust_repository.load_for_environment("production")
# These two repositories are application-owned adapters.
# Treat fetched bundle bytes and registry responses as untrusted input. Pin issuer and auditor keys through TrustConfig. Never infer
trust merely because a bundle names a familiar issuer.
4.4 — Verify and enforce
from vcp import (
BundleExpiryPlugin,
Orchestrator,
PDPEnforcer,
RefusalBoundaryPlugin,
VerificationResult,
)
verification = Orchestrator(trust).verify(bundle)
if verification is not VerificationResult.VALID:
raise PermissionError(
"VCP bundle rejected: " + verification.name
)
enforcer = PDPEnforcer(fail_closed=True)
enforcer.register(BundleExpiryPlugin())
enforcer.register(RefusalBoundaryPlugin())
policy = enforcer.evaluate(
bundle,
content=user_message,
verification_result=verification,
)
if not policy.allowed:
raise PermissionError("VCP policy blocked this request") Verification asks whether the bundle is genuine and currently valid.
Enforcement asks whether
this particular request is allowed under it. The built-in enforcer
produces ALLOW, BLOCK, MODIFY, or ESCALATE decisions, and the example above treats every
decision except ALLOW as a refusal, MODIFY included. The SDK 4.2.0 enforcer has no escalation handler, so a plugin's ESCALATE makes the final decision BLOCK, and the ESCALATE stays in the result's decision list for your
application to act on. Applications that need human review must build and
test that workflow explicitly.
4.5 — Format only after verification
from vcp import InjectionFormat, InjectionOptions, format_injection
vcp_prompt = format_injection(
bundle,
options=InjectionOptions(
format=InjectionFormat.HEADER_DELIMITED,
include_tokens=True,
include_attestation=True,
),
)
messages = [
{"role": "system", "content": vcp_prompt},
{"role": "user", "content": user_message},
]
# Send messages through your provider adapter only after every gate above passes.
reply = await model_provider.respond(messages) Important:
format_injectionformats a bundle. Its type signature cannot prove that your application calledverifyfirst. Preserve that ordering in one reviewed function and cover the negative path with tests.
4.6 — Add adaptation context
Context about the conversation, such as the time, the place and who is present, is encoded separately from the constitution with the same encoder you met in Module 3. The bundle arrives signed and passes verification; the context wire value carries no signature and passes none of the gates above:
from vcp.adaptation import ContextEncoder
context = ContextEncoder().encode(
time="evening",
space="home",
company="children",
)
context_wire = context.encode()
# Your application decides whether and where this bounded wire value is shared. Context does not acquire confidentiality merely because it is encoded. Apply consent, minimization, platform filtering, retention limits, and logging redaction at the application boundary.
4.7 — Record verification evidence
from vcp import AuditLevel, AuditLogger
audit = AuditLogger(level=AuditLevel.STANDARD)
audit.log_verification(
bundle,
verification,
session_id=session_id,
request_id=request_id,
)
# Persist through a controlled callback or export path with your own
# access control and retention policy. The SDK hashes session and request identifiers before storing them. That reduces exposure but does not remove your obligations around access, retention, deletion, and incident response.
4.8 — Test the failure paths
- Missing bundle and missing trust anchor
- Hash mismatch, invalid signature, and invalid attestation
- Expired, not-yet-valid, revoked, and replayed bundles
- Scope or audience mismatch
- Enforcement plugin exception under fail-closed operation
- Provider failure after verification, without leaking raw private context into logs
Exercise
Run the SDK's examples/python/05_full_pipeline.py. Then
tamper with the bundle content and confirm that verification fails
before formatting or model invocation.
See it in action
The Gentian demo shows one person's constraints reaching three simulated music platforms. It does not verify signed bundles, and it is an explanatory application, not evidence that a third-party platform implements VCP.