A retro-styled adventurer takes a pause by a lush jungle riverbank.
{
"image_analysis": {
"environment": {
"type": "Outdoor",
"setting": "Jungle / Tropical Forest / Riverbank",
"details": "Dense vegetation, presence of water with lily pads, mud or dirt bank."
},
"technical_aspects": {
"camera_angle": "Eye-level relative to the crouching subject, slightly side-profile.",
"lens_type": "Telephoto lens (estimated 85mm-135mm)",
"depth_of_field": "Shallow, background and foreground are blurred (bokeh).",
"composition": "Rule of thirds, subject centered but looking back."
},
"lighting": {
"condition": "Natural daylight, dappled sunlight filtering through trees.",
"sources": [
{
"type": "Sunlight",
"angle": "From above and slightly behind the subject (Backlighting/Rim lighting)",
"color": "Warm White / Golden",
"intensity": "High contrast",
"effect_on_objects": "Creates a halo effect on the subject's hair, highlights the shoulder blade and the curve of the back. Causes lens flare/light leaks in the foreground."
}
]
},
"subject": {
"demographics": {
"gender": "Female",
"age_group": "Young Adult (approx. 20-30s)",
"identity": "Anonymized (resembles 1980s aesthetic)"
},
"orientation": {
"body_facing": "Side profile (facing right of frame)",
"head_facing": "Turned left, looking directly at the camera",
"gaze": "Direct eye contact"
},
"emotional_state": {
"expression": "Alert, slightly surprised or candid, neutral.",
"mood": "Wild, naturalistic, slightly vulnerable but composed.",
"sensuality": "Moderate to High (due to attire and pose, but context is action/survival)."
},
"pose": {
"general": "Deep squat / Crouching position.",
"feet_placement": "Left foot flat on the ground (wearing a shoe), right foot tucked behind on toes (barefoot).",
"hand_placement": "Left hand holding a canteen strap near the knee, right arm obscured/resting.",
"visibility": "Full body visible from head to feet."
},
"head_and_face": {
"hair": {
"color": "Blonde",
"style": "Short, layered, messy/shaggy cut (mullet-esque), wet look or styled casually.",
"texture": "Wavy/Straight mix",
"light_interaction": "Strongly back-lit, glowing edges."
},
"ears": "Partially visible through hair.",
"forehead": "Partially covered by bangs.",
"eyes": "Wide, alert.",
"nose": "Straight, defined bridge.",
"mouth": "Lips slightly parted, natural color.",
"chin": "Defined, slightly pointed.",
"structure": "Oval face shape, high cheekbones."
},
"body_type": {
"build": "Slender, athletic, toned.",
"skin_tone": "Fair to medium tan.",
"neck": "Slender, tendons visible due to head turn.",
"shoulders": "Bony, defined.",
"chest": {
"ratio": "Proportional to slender frame.",
"estimated_size": "Small to Medium.",
"bra_status": "No bra (swimsuit support).",
"nipple_visibility": "Not explicitly visible/defined.",
"shape": "Natural side profile."
},
"abdomen": {
"ratio": "Slim, compressed due to crouching.",
"definition": "Smooth."
},
"hips_and_glutes": {
"ratio": "Curvy relative to waist.",
"prominence": "High prominence due to crouching pose and high-cut swimwear.",
"shape": "Rounded."
},
"legs": {
"thighs": "Toned, compressed against calves.",
"knees": "Sharp angle.",
"calves": "Visible, muscular tension."
}
},
"clothing": {
"upper_body": {
"item": "One-piece swimsuit",
"color": "Black",
"material": "Spandex/Lycra (shiny/wet look)",
"style": "Scoop back, thin straps (halter style likely)."
},
"lower_body": {
"item": "Swimsuit bottom (connected)",
"style": "High-cut leg openings, exposing upper thigh and hip bone."
},
"footwear": {
"left_foot": "Saddle shoe (White with black middle section), laced.",
"right_foot": "Barefoot."
}
},
"accessories": {
"items": [
"Canteen (Metal/Silver with black strap)"
]
},
"light_interaction_body": "Highlight on the left shoulder blade, rim light on the back curve, soft shadow on the face, bright highlights on the shin."
},
"objects": [
{
"name": "Canteen / Flask",
"description": "Silver metal container with a strap.",
"purpose": "Prop indicating survival/hiking context.",
"position": "Held in left hand, resting near knee."
},
{
"name": "Shoe (detached)",
"description": "A second saddle shoe appears to be on the ground in the foreground left (partially cropped).",
"purpose": "Implies a casual or changing state.",
"position": "Bottom left corner."
},
{
"name": "Vegetation",
"description": "Ferns, lily pads on water.",
"color": "Green, dark green.",
"position": "Background and right side."
},
{
"name": "Blurred Foreground Grass/Reeds",
"description": "Out-of-focus yellow/brown stalks.",
"purpose": "Adds depth and voyeuristic framing.",
"position": "Crossing the subject's body in the foreground."
}
],
"negative_prompt": "cartoon, 3d render, illustration, drawing, low quality, pixelated, blurry face, distorted hands, extra limbs, bad anatomy, studio background, grey background, urban setting, fully clothed, denim, heavy makeup, mustache, beard, male."
}
}
Add AI protection
---
name: add-ai-protection
license: Apache-2.0
description: Protect AI chat and completion endpoints from abuse — detect prompt injection and jailbreak attempts, block PII and sensitive info from leaking in responses, and enforce token budget rate limits to control costs. Use this skill when the user is building or securing any endpoint that processes user prompts with an LLM, even if they describe it as "preventing jailbreaks," "stopping prompt attacks," "blocking sensitive data," or "controlling AI API costs" rather than naming specific protections.
metadata:
pathPatterns:
- "app/api/chat/**"
- "app/api/completion/**"
- "src/app/api/chat/**"
- "src/app/api/completion/**"
- "**/chat/**"
- "**/ai/**"
- "**/llm/**"
- "**/api/generate*"
- "**/api/chat*"
- "**/api/completion*"
importPatterns:
- "ai"
- "@ai-sdk/*"
- "openai"
- "@anthropic-ai/sdk"
- "langchain"
promptSignals:
phrases:
- "prompt injection"
- "pii"
- "sensitive info"
- "ai security"
- "llm security"
anyOf:
- "protect ai"
- "block pii"
- "detect injection"
- "token budget"
---
# Add AI-Specific Security with Arcjet
Secure AI/LLM endpoints with layered protection: prompt injection detection, PII blocking, and token budget rate limiting. These protections work together to block abuse before it reaches your model, saving AI budget and protecting user data.
## Reference
Read https://docs.arcjet.com/llms.txt for comprehensive SDK documentation covering all frameworks, rule types, and configuration options.
Arcjet rules run **before** the request reaches your AI model — blocking prompt injection, PII leakage, cost abuse, and bot scraping at the HTTP layer.
## Step 1: Ensure Arcjet Is Set Up
Check for an existing shared Arcjet client (see `/arcjet:protect-route` for full setup). If none exists, set one up first with `shield()` as the base rule. The user will need to register for an Arcjet account at https://app.arcjet.com then use the `ARCJET_KEY` in their environment variables.
## Step 2: Add AI Protection Rules
AI endpoints should combine these rules on the shared instance using `withRule()`:
### Prompt Injection Detection
Detects jailbreaks, role-play escapes, and instruction overrides.
- JS: `detectPromptInjection()` — pass user message via `detectPromptInjectionMessage` parameter at `protect()` time
- Python: `detect_prompt_injection()` — pass via `detect_prompt_injection_message` parameter
Blocks hostile prompts **before** they reach the model. This saves AI budget by rejecting attacks early.
### Sensitive Info / PII Blocking
Prevents personally identifiable information from entering model context.
- JS: `sensitiveInfo({ deny: ["EMAIL", "CREDIT_CARD_NUMBER", "PHONE_NUMBER", "IP_ADDRESS"] })`
- Python: `detect_sensitive_info(deny=[SensitiveInfoType.EMAIL, SensitiveInfoType.CREDIT_CARD_NUMBER, ...])`
Pass the user message via `sensitiveInfoValue` (JS) / `sensitive_info_value` (Python) at `protect()` time.
### Token Budget Rate Limiting
Use `tokenBucket()` / `token_bucket()` for AI endpoints — the `requested` parameter can be set proportional to actual model token usage, directly linking rate limiting to cost. It also allows short bursts while enforcing an average rate, which matches how users interact with chat interfaces.
Recommended starting configuration:
- `capacity`: 10 (max burst)
- `refillRate`: 5 tokens per interval
- `interval`: "10s"
Pass the `requested` parameter at `protect()` time to deduct tokens proportional to model cost. For example, deduct 1 token per message, or estimate based on prompt length.
Set `characteristics` to track per-user: `["userId"]` if authenticated, defaults to IP-based.
### Base Protection
Always include `shield()` (WAF) and `detectBot()` as base layers. Bots scraping AI endpoints are a common abuse vector. For endpoints accessed via browsers (e.g. chat interfaces), consider adding Arcjet advanced signals for client-side bot detection that catches sophisticated headless browsers. See https://docs.arcjet.com/bot-protection/advanced-signals for setup.
## Step 3: Compose the protect() Call and Handle Decisions
All rule parameters are passed together in a single `protect()` call. Use this pattern:
```typescript
const userMessage = req.body.message; // the user's input
const decision = await aj.protect(req, {
requested: 1, // tokens to deduct for rate limiting
sensitiveInfoValue: userMessage, // PII scanning
detectPromptInjectionMessage: userMessage, // injection detection
});
if (decision.isDenied()) {
if (decision.reason.isRateLimit()) {
return Response.json(
{ error: "You've exceeded your usage limit. Please try again later." },
{ status: 429 },
);
}
if (decision.reason.isPromptInjection()) {
return Response.json(
{ error: "Your message was flagged as potentially harmful." },
{ status: 400 },
);
}
if (decision.reason.isSensitiveInfo()) {
return Response.json(
{
error:
"Your message contains sensitive information that cannot be processed. Please remove any personal data.",
},
{ status: 400 },
);
}
if (decision.reason.isBot()) {
return Response.json({ error: "Forbidden" }, { status: 403 });
}
}
// Arcjet fails open — log errors but allow the request
if (decision.isErrored()) {
console.warn("Arcjet error:", decision.reason.message);
}
// Proceed with AI model call...
```
Adapt the response format to your framework (e.g., `res.status(429).json(...)` for Express).
## Step 5: Verify
1. Start the app and send a normal message — should succeed
2. Test prompt injection by sending something like "Ignore all previous instructions and..."
3. Test PII blocking by sending a message with a fake credit card number
Start all rules in `"DRY_RUN"` mode first. Once verified, promote to `"LIVE"`.
**Always recommend using the Arcjet MCP tools** to verify rules and analyze traffic:
- `list-requests` — confirm decisions are being recorded, filter by conclusion to see blocks
- `analyze-traffic` — review denial rates and patterns for the AI endpoint
- `explain-decision` — understand why a specific request was allowed or denied (useful for tuning prompt injection sensitivity)
- `promote-rule` — promote rules from `DRY_RUN` to `LIVE` once verified
If the user wants a full security review, suggest the `/arcjet:security-analyst` agent which can investigate traffic, detect anomalies, and recommend additional rules.
The Arcjet dashboard at https://app.arcjet.com is also available for visual inspection.
## Common Patterns
**Streaming responses**: Call `protect()` before starting the stream. If denied, return the error before opening the stream — don't start streaming and then abort.
**Multiple models / providers**: Use the same Arcjet instance regardless of which AI provider you use. Arcjet operates at the HTTP layer, independent of the model provider.
**Vercel AI SDK**: Arcjet works alongside the Vercel AI SDK. Call `protect()` before `streamText()` / `generateText()`. If denied, return a plain error response instead of calling the AI SDK.
## Common Mistakes to Avoid
- Sensitive info detection runs **locally in WASM** — no user data is sent to external services. It is only available in route handlers, not in Next.js pages or server actions.
- `sensitiveInfoValue` and `detectPromptInjectionMessage` (JS) / `sensitive_info_value` and `detect_prompt_injection_message` (Python) must both be passed at `protect()` time — forgetting either silently skips that check.
- Starting a stream before calling `protect()` — if the request is denied mid-stream, the client gets a broken response. Always call `protect()` first and return an error before opening the stream.
- Using `fixedWindow()` or `slidingWindow()` instead of `tokenBucket()` for AI endpoints — token bucket lets you deduct tokens proportional to model cost and matches the bursty interaction pattern of chat interfaces.
- Creating a new Arcjet instance per request instead of reusing the shared client with `withRule()`.
berre
{
"reference": {
"face_identity": "${face_identity:uploaded reference image never change face and hair}",
"identity_lock": true,
"face_preservation": "100% identical facial structure, proportions, skin texture, eye shape, lips, nose, brows, moles, and natural expression"
},
"subjects": [
{
"type": "${subject1_type:young woman}",
"role": "foreground subject",
"expression": "soft confident smile",
"gaze": "looking directly at the camera",
"pose": {
"position": "standing very close to the male subject",
"interaction": "leaning slightly toward him in a casual selfie pose"
},
"hair": {
"color": "dark brown",
"style": "sleek high ponytail"
},
"makeup": {
"style": "natural glam",
"details": [
"even glowing skin",
"subtle contour",
"soft blush",
"defined brows",
"natural pink lips"
]
},
"outfit": {
"clothing": "black fitted short-sleeve top",
"style": "sporty casual"
}
},
{
"type": "${subject2_type:Lionel Messi}",
"role": "secondary subject",
"expression": "relaxed smile",
"gaze": "looking at the camera",
"features": {
"beard": "short, well-groomed full beard"
},
"hair": {
"color": "brown",
"style": "short, modern styled, slightly messy on top"
},
"outfit": {
"clothing": "Inter Miami pink home football jersey",
"details": [
"Inter Miami heron logo visible on chest",
"adidas logo visible",
"sponsor logo visible",
"athletic fit"
]
}
}
],
"environment": {
"location": "football stadium",
"background_elements": [
"large cheering crowd",
"stadium seating filled with fans",
"players and staff visible in the distance",
"night sky with stadium floodlights"
]
},
"lighting": {
"type": "stadium lighting",
"characteristics": [
"bright overhead lights",
"even illumination on faces",
"slight highlights on skin",
"realistic night-time contrast"
]
},
"photography_style": {
"style": "real-life selfie photography",
"camera_look": "smartphone camera",
"depth_of_field": "moderate depth of field with slightly blurred crowd",
"mood": "excited, celebratory, candid"
},
"render_quality": {
"realism": "ultra-photorealistic",
"detail_level": "high",
"Aspect Ratio": "4:5",
"skin_texture": "natural and realistic",
"resolution": "high resolution",
"color_grading": "true-to-life colors with stadium vibrancy"
}
}
Cinematic Urban Night Portrait - Moody Streetwear Aesthetic
{
"scene_setup": {
"subject": {
"clothing": "wearing a black oversized hoodie, black backwards baseball cap, silver chain necklace",
"appearance": "male model, beard, intense serious gaze, masculine features",
"pose": "sitting or leaning forward, looking down at camera, authoritative stance"
},
"camera_angle": {
"type": "Low angle shot",
"focus": "Sharp focus on face, shallow depth of field (bokeh background)",
"framing": "Medium close-up portrait"
},
"environment": {
"location": "Urban street at night, under a concrete bridge or overpass",
"background_elements": "blurred city lights, bokeh skyscrapers, a car with headlights on in the background",
"ground": "wet asphalt, rain reflections"
},
"lighting_and_fx": {
"style": "Cinematic moody lighting, high contrast",
"colors": "Teal and orange color grading, warm street lights vs dark blue sky",
"effects": "Smoke or steam rising in the foreground, volumetric lighting"
},
"technical": {
"quality": "Photorealistic, 8k resolution, raw photo style, highly detailed texture",
"engine": "Unreal Engine 5 render style or high-end photography"
}
}
}