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

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages