Skip to content

millingtonsully/masksdkagent

Repository files navigation

Secure Email Agent with Mask SDK

License

A reference implementation for a privacy-first AI agent. It uses the Mask SDK to protect PII before it reaches cloud LLMs, while keeping tools (like Gmail) 100% functional via Transparent Detokenization.

Why Mask?

Standard agents leak PII into cloud context windows. Redaction breaks tools. Mask solves this via Just-In-Time (JIT) Encryption:

  1. Intercept: Input is scanned locally; PII is replaced with cryptographic tokens.
  2. Reason: The LLM reasons over the tokenized prompt (zero-knowledge).
  3. Execute: Authorized tools (e.g., send_email) automatically decrypt tokens via the @secure_tool hook to run with real data.

Architecture & Setup

Component Responsibility
agent.py Orchestration, Mask initialization, and Tool definitions.
gmail_client.py Wrapper for OAuth 2.0 and Gmail API execution.
.env API Keys (Model-agnostic).

1. Prerequisites

  • Install uv
  • Configure Gmail API: Enable the API in GCP Console, set up an OAuth Desktop app, and save the JSON as credentials.json.

2. Quick Start

uv sync
uv run agent.py

Note: Run python -m spacy download en_core_web_sm and es_core_news_sm if needed for the local engine.


Agent Execution

Run the agent to witness token-in, tool-out privacy:

  1. Authorize: A browser opens on first run to link your Gmail.
  2. Interact: Provide a prompt with PII.
  3. Audit: Watch the logs to see tokens sent to the LLM and real data securely passed to the Gmail API.

Under the Hood: The Token Lifecycle

If you observe the agent's raw LangGraph trace during execution, you will see exactly how Mask intercepts and encrypts data in-flight. Below is a condensed snippet of the raw output demonstrating the LLM's blindness to the PII:

[llm/start] [chain:LangGraph > chain:agent > chain:RunnableSequence > llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "...Human: Encuentra el correo de [TKN-b568c6c6] y envíale su nueva contraseña 'Secret123'. \nMenciona que mi número de DNI para la validación es [TKN-483155ce]."
  ]
}

...

[tool/start] [chain:LangGraph > chain:tools > tool:search_contacts] Entering Tool run with input:
"{'name': '[TKN-b568c6c6]'}"
[tool/end] [chain:LangGraph > chain:tools > tool:search_contacts] [156ms] Exiting Tool run with output:
"content='tkn-686c625c@email.com' name='search_contacts' tool_call_id='call_a6THF2EMn4N279bvBNkYq4Dv'"

...

[tool/start] [chain:LangGraph > chain:tools > tool:send_email] Entering Tool run with input:
"{'to_email': 'tkn-686c625c@email.com', 'subject': 'Nueva Contraseña y Validación de DNI', 'body': "Hola,\n\nTu nueva contraseña es 'Secret123'. Por favor, utiliza mi número de DNI [TKN-483155ce] para la validación.\n\nSaludos."}"

[Gmail API] Email successfully sent to 'millingtonsully@gmail.com' (Message ID: 19d56bfc7e282eba)

[tool/end] [chain:LangGraph > chain:tools > tool:send_email] [2.43s] Exiting Tool run with output:
"content='Successfully sent email to tkn-c23295f9@email.com' name='send_email' tool_call_id='call_bKiihx1yGbffKCcdpRR31umG'"

Next Steps & Security

LLM Agnostic

The agent uses LangChain. To switch providers (e.g., to Claude), swap the LLM instantiation in agent.py:

from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-3-5-sonnet")

Production Hardening

  1. Service Accounts: Replace user OAuth with Google Cloud Service Accounts for server environments.
  2. Key Management: Store MASK_ENCRYPTION_KEY in a secure vault (e.g., AWS KMS).
  3. Audits: Redirect MaskCallbackHandler output to a centralized logging system.

License

Apache License, Version 2.0. Copyright (c) 2026 Mask AI Solutions.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages