Prompt Engineering
Contexa turns every authenticated HTTP request — whether from a human or an agent — into a structured prompt an LLM can reason about consistently. Getting there takes six stages: collect → process → harden → standardise → compose → calibrate. This page follows the flow end to end.
Six-stage pipeline
A prompt is never built on the spot. Six stages collect, organise and verify every fact around the request before anything reaches the LLM. Each stage lives in its own package and the output of one stage is the input of the next.
Stage 1 · Collect
Once a request enters the Spring Security filter chain, an AuthBridge implementation pulls the principal out of headers, session attributes or request attributes. The extracted evidence is packaged into three kinds of evidence stamps. Those three stamps are the raw input for every later stage.
- principalType · subject class
- authenticationType
- authenticationAssurance
- mfaCompleted
- authenticationSource
- sessionId · authenticationTime
- effect (ALLOW · DENY · UNKNOWN)
- privileged action flag
- policyId · policyVersion
- decisionSource
- effectiveRoles · effectiveAuthorities
- subjectId · the human delegator
- agentId · the acting principal
- objectiveId · objectiveFamily
- allowedOperations
- allowedResources
- containmentOnly · expiresAt
Five evidence quality tiers
Not all evidence carries the same weight. A signal the customer sent explicitly is the strongest; a platform-observed structural signal comes next; a fallback derived from runtime continuity is the weakest. BridgeSemanticBoundaryPolicy tags every collected field with one of the tiers below.
Stage 2 · Process
Raw evidence alone is not enough for the LLM to decide whether a user is acting differently from usual. Stage 2 expands it through three families of components that turn primitive facts into observable patterns.
- SessionNarrativeCollector — in-session action sequence
- ProtectableWorkProfileCollector — usual work profile
- RoleScopeCollector — current role scope snapshot
- AuthenticationContextProvider — auth strength
- DelegationContextProvider — delegation scope
- PeerCohortContextProvider — cohort outliers
- FrictionContextProvider — MFA & approval history
- OrganizationContextProvider — org hierarchy
- ReasoningMemoryContextProvider — prior cases
- ObjectiveDriftEvaluator — agent objective drift
- ObservedScopeInferenceService — observed scope
- ContextCoverageEvaluator — coverage level
ObjectiveDriftEvaluator checks in real time whether the request's resource and action family fall inside the allowedOperations and allowedResources declared by the delegation stamp.
Stage 3 · Harden
Prompt injection, field spoofing and signal forgery are the top threats for any LLM-based security system. CanonicalSecurityContextHardener puts every field through field-specific normalisation before it ever reaches the model.
- Replace null fields with safe defaults
- Trim strings and cap length
- Normalise enum values
- Validate time and coordinate ranges
- Normalise language / country codes to ISO
- Decode payloads and filter to 80%+ printable chars
- Preserve session narrative burst flags
- Drop negative values from the work profile
- Deduplicate role scope while keeping order
- Pin approval-lineage order in friction
- Clean reasoning-memory guardrails
- Normalise trust profile decision states
Stage 4 · Standardise
After stage 3, every piece of evidence is consolidated into a single standard context model — CanonicalSecurityContext. The current implementation is a Lombok-backed class; the hardener normalizes fields and fills required defaults before the compose stage receives it. Twenty-two fields and seven profiles sit in well-known places, so the compose stage always knows where each field lives.
Subject
- userId
- organizationId
- tenantId
- principalType
- roleSet
- authoritySet
Session
- sessionId
- clientIp
- userAgent
- authenticationType
- mfaVerified
- concurrentSessions
Delegation
- delegated
- agentId
- objectiveId
- allowedOperations
- allowedResources
- objectiveDrift
Authorization
- effectiveRoles
- effectivePermissions
- scopeTags
- authorizationEffect
- policyId
- policyVersion
Intent
- botUserAgent
- impossibleTravel
- intentMissingReferer
Location · IP
- country · city
- latitude · longitude
- IP band · ASN
Resource
- resourceType
- businessLabel
- sensitivity
- actionFamily
Seven profiles
- SessionNarrative
- Work
- RoleScope
- PeerCohort
- Friction
- ReasoningMemory
- ObservedScope
Four coverage levels
ContextCoverageEvaluator grades how complete the record is by counting filled fields and available profiles. A low grade automatically injects a warning into the prompt telling the LLM not to draw strong conclusions from "usual work patterns".
Stage 5 · Compose
The standardised context now becomes a prompt. SecurityDecisionPromptSections orchestrates the work and renders 2 system sections plus 15 user sections, for 17 section plans in total. When a field is missing, the builder for that section simply emits nothing.
System side and user side
Tells the LLM its role, the policy, and the expected output format. Stable across requests.
- Security policy instructions
- Format and length constraints
- JSON schema enforcement
- Governance version stamp
Lists the evidence for the current request, section by section. Changes per request.
- Current event
- Canonical context and coverage
- Identity and authority
- Device, location, session
- Delegation, friction, behaviour profile
- Threat knowledge, organisational learning, and explicit missing knowledge
17 section plans
CanonicalContextFieldPolicy.has*(), while the missing-knowledge section decides whether to render from the coverage report and trust profiles. If a field is absent, the whole section is dropped, so the prompt always carries only "honest" information. PromptGovernanceDescriptor stamps the model and version for reproducibility.
EXPLICIT_MISSING_KNOWLEDGE
EXPLICIT_MISSING_KNOWLEDGE is a P0 required quality indicator section that prevents the LLM from mistaking evidence gaps for certainty. SecurityContextQualityUserSectionBuilder calls PromptContextComposer.composeMissingKnowledgeSection() and renders the section only when coverage gaps or trust-profile evidence cautions exist. If no gap signal exists, the section remains empty; that means there is no explicit missing knowledge to declare.
- Coverage gaps —
missingCriticalFacts,remediationHints, orconfidenceWarningscreate the missing-knowledge section. - Trust limits —
ContextEvidenceLimitation,ContextTrustLimitation,ContextTrustWarning,ContextFieldCoverage, andContextFieldLimitationare surfaced as explicit items. - False-positive control — stale
AUTHORIZATION_EFFECTmissing-context warnings are suppressed once the authorization effect has been resolved. - Baseline support — sparse personal or organisational baselines add
BaselineGapSupportto remind the model that missing evidence is not proof of either risk or legitimacy. - Compression preservation — compact budgets prioritise
BaselineGapSupport,ConfidenceWarning,ContextEvidenceLimitation,ContextTrustLimitation, andContextTrustWarning. If the budget still overflows, summarised or omitted details are recorded in the compression ledger.
Stage 6 · Calibrate
Getting a response back from the LLM is not the end. Two safety layers adjust the result in sequence: the autonomy guardrail and the runtime calibration.
Autonomy guardrail
If the LLM's confidence is below a threshold or the output deviates from the schema, PromptConfidenceGuardrail forces the final action up to a safer tier. The proposed action and the enforced action are stored separately to keep the audit trail intact.
Runtime calibration
Decisions the guardrail did not touch are fine-tuned by SecurityDecisionCalibrationService using learned profiles. Confidence adjustments and action biases are applied based on past observations.
- Scenario classification — group the current request with past similar scenarios (for example, unfamiliar location + unusual resource)
- Profile selection — pick only profiles that have enough samples and operator review
- Action bias — one of INCREASE_CHALLENGE · DECREASE_CHALLENGE · NONE
- BLOCK is immutable — a BLOCK decision is never changed by a bias (safety first)
- Guardrail wins — if the guardrail already intervened, calibration is skipped
The final decision record
What comes out the other end is the SecurityDecision. It is not just "ALLOW" or "BLOCK". It is an auditable record that carries the LLM's raw judgement, the policy intervention, and the learning calibration — all three.
SecurityDecision fields
How prompts differ for humans and agents
The pipeline is the same, but the centre of gravity shifts depending on who the subject is. For a human, the key axis is "deviation from the usual pattern". For an agent, it is "staying within the allowed objective".
| Subject type | HUMAN |
|---|---|
| Delegation stamp | Usually absent |
| Key profiles | Work · PeerCohort · Friction |
| Key sections | IdentityAuthority · BehaviorProfile |
| Decision axis | Deviation from usual behaviour |
| MFA signal | Verified at login time |
| Subject type | AGENT · SERVICE_ACCOUNT |
|---|---|
| Delegation stamp | Required. delegated = true + agentId |
| Key profiles | ObservedScope · RoleScope |
| Key sections | Delegation · ObjectiveDrift |
| Decision axis | Drift from the allowed objective |
| MFA signal | Verified when the delegation token was issued |