SmartField Documentation

Every keystroke, encrypted. Invisible to JavaScript, trackers, and hackers.

Quick Start. 2 Minutes

Step 1: Add the script

<script src="https://smartfield.dev/component/smartfield.js"></script>

Step 2: Replace your inputs

<!-- Before (vulnerable) -->
<input type="password" name="password">

<!-- After (encrypted) -->
<smart-field
  type="password"
  encrypt-key="/sf-key"
  sf-security="brief"
  sf-stealth
  placeholder="..."
></smart-field>

encrypt-key is required. it tells SmartField where to fetch your server's public key. Use a relative URL (e.g. /sf-key).

Step 3: Decrypt on your server

// Node.js
const sf = require('@smartfield-dev/server');
await sf.init();

// Serve public key (SmartField fetches this automatically)
app.get('/sf-key', (req, res) => res.json(sf.getPublicKey()));

// Decrypt. fields arrive with random IDs, not "password"
app.post('/login', async (req, res) => {
  const data = await sf.decryptFields(req.body);
  // data = { sf_a7f3c2: "user@email.com", sf_b9e1d4: "mypassword" }
  const values = Object.values(data);
  const email = values[0];
  const password = values[1];
});
That's it. Your form is now encrypted end-to-end. Every keystroke is protected with AES-256-GCM + RSA-2048.

Common Mistakes. Don't Do This

MISTAKE #1: Using descriptive field names

<smart-field name="password"> ← WRONG
<smart-field name="credit_card"> ← WRONG
<smart-field name="ssn"> ← WRONG

SmartField automatically generates random IDs (sf_a7f3c2e1). If you set name="password", an attacker can identify which field is the password by reading the DOM attribute. Don't name your fields. SmartField handles field identification internally.
MISTAKE #2: Reading .value and expecting plaintext

const pwd = document.querySelector('smart-field').value;
This returns encrypted data, not the password. You MUST decrypt on the server.
MISTAKE #3: Using console.log on decrypted values

console.log("Password:", decryptedPassword); ← NEVER

This defeats the purpose. Decrypted values should only be used for authentication/processing, never logged.
MISTAKE #4: Storing the private key in your frontend code

The private key MUST stay on your server. Never include it in JavaScript, environment variables accessible to the frontend, or client-side code.
MISTAKE #5: Not using sf-type for structured data

<smart-field> ← For card numbers, use sf-type
<smart-field sf-type="card"> ← Validates 16 digits, blocks letters

Without sf-type, the field accepts any input. Use sf-type for cards, expiry, CVV, SSN, phone.
MISTAKE #6: Using absolute HTTP URLs for encrypt-key

<smart-field encrypt-key="http://myserver.com/api/key"> ← BLOCKED

SmartField blocks http:// URLs in production for security. Use relative URLs instead:

<smart-field encrypt-key="/api/public-key"> ← CORRECT

Relative URLs automatically use your page's protocol (HTTPS). This ensures the key exchange is always encrypted.
MISTAKE #7: Forgetting to clear sessionStorage after adding data-key

If you tested without a data-key first (free plan, 3 fields max), and then add a key, the free license stays cached for 24 hours. Clear it:

sessionStorage.removeItem('sf_license')

Or tell your users to hard-refresh (Ctrl+Shift+R).
MISTAKE #8: Copying license keys between environments

License keys are generated per server. A key created on localhost won't work in production. Always generate keys in the environment where they'll be used:

sf.init() → generates keys on YOUR server
license.generateKey() → generates license on YOUR API server
BEST PRACTICE: Field mapping on the server

SmartField sends random IDs (sf_a7f3c2, sf_b9e1d4). Your server maps these to field names by their order in the form:

Field 1 → email
Field 2 → password

Or use the server SDK's decryptFields() which handles this automatically.

Server Setup

Choose your language. All SDKs use the same encryption format and key management.

Node.js Node.js

npm install @smartfield-dev/server
const sf = require('@smartfield-dev/server');
const express = require('express');
const app = express();
app.use(express.json());

// Initialize. generates keys on YOUR server
await sf.init();

// Serve public key to frontend
app.get('/sf-key', (req, res) => res.json(sf.getPublicKey()));

// Decrypt form submission
app.post('/login', async (req, res) => {
  const fields = await sf.decryptFields(req.body);
  // fields = { sf_a7f3c2: "user@email.com", sf_b9e1d4: "password123" }

  const email = Object.values(fields)[0];    // first field
  const password = Object.values(fields)[1]; // second field

  // Authenticate...
});

// Or use middleware (auto-serves key + auto-decrypts)
app.use(sf.middleware());

Python Python

pip install smartfield
from smartfield import SmartField
from flask import Flask, request, jsonify

app = Flask(__name__)
sf = SmartField()
sf.init()  # generates keys in .smartfield/

@app.route('/sf-key')
def public_key():
    return jsonify(sf.get_public_key())

@app.route('/login', methods=['POST'])
def login():
    data = request.json
    email = sf.decrypt(data['email'])
    password = sf.decrypt(data['password'])

    # Authenticate...
    return jsonify({"status": "ok"})

Java Java / Spring

// Maven
<dependency>
  <groupId>dev.smartfield</groupId>
  <artifactId>smartfield-server</artifactId>
  <version>0.1.0</version>
</dependency>
import dev.smartfield.SmartField;

@RestController
public class AuthController {

    private final SmartField sf = new SmartField();

    @PostConstruct
    public void init() {
        sf.init(); // generates keys in .smartfield/
    }

    @GetMapping("/sf-key")
    public Map<String, Object> publicKey() {
        return sf.getPublicKey();
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody Map<String, String> body) {
        String email = sf.decrypt(body.get("email"));
        String password = sf.decrypt(body.get("password"));

        // Authenticate...
        return ResponseEntity.ok().build();
    }
}

PHP PHP / Laravel

composer require smartfield/server
use SmartField\SmartField;

$sf = new SmartField();
$sf->init(); // generates keys in .smartfield/

// Laravel route
Route::get('/sf-key', function() use ($sf) {
    return response()->json($sf->getPublicKey());
});

Route::post('/login', function(Request $request) use ($sf) {
    $email = $sf->decrypt($request->input('email'));
    $password = $sf->decrypt($request->input('password'));

    // Authenticate...
});

Go Go

go get github.com/smartfield/smartfield-go
package main

import (
    "github.com/smartfield/smartfield-go"
    "net/http"
    "encoding/json"
)

func main() {
    sf := smartfield.New()
    sf.Init() // generates keys in .smartfield/

    http.HandleFunc("/sf-key", func(w http.ResponseWriter, r *http.Request) {
        json.NewEncoder(w).Encode(sf.GetPublicKey())
    })

    http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
        var body map[string]string
        json.NewDecoder(r.Body).Decode(&body)

        email := sf.Decrypt(body["email"])
        password := sf.Decrypt(body["password"])

        // Authenticate...
    })

    http.ListenAndServe(":8080", nil)
}

Ruby / Rails Ruby

gem install smartfield
require 'smartfield'

sf = SmartField.new
sf.init # generates keys in .smartfield/

# Rails controller
class AuthController < ApplicationController
  def public_key
    render json: sf.get_public_key
  end

  def login
    email = sf.decrypt(params[:email])
    password = sf.decrypt(params[:password])

    # Authenticate...
  end
end

Frontend Setup

Vanilla HTML

<script src="https://smartfield.dev/component/smartfield.js"
        data-key="sf_live_your_key_here"></script>

<form action="/login" method="POST">
  <smart-field type="email" placeholder="Email"
    encrypt-key="/sf-key"></smart-field>

  <smart-field type="password" placeholder="Password"
    encrypt-key="/sf-key"></smart-field>

  <button type="submit">Log In</button>
</form>

React / Next.js

WARNING: Do NOT use <smart-field> directly in JSX.

React can destroy and re-create DOM elements on state changes. This kills SmartField's Shadow DOM and WeakMap data. You MUST use the official React wrapper below.

Step 1: Add the script to your layout

// app/layout.tsx (Next.js App Router)
export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <script src="https://smartfield.dev/component/smartfield.js" defer></script>
      </head>
      <body>{children}</body>
    </html>
  );
}

Step 2: Create the SmartField wrapper component

// components/SmartField.tsx
"use client";
import { useRef, useEffect, memo } from "react";

const _values = new Map<string, string>();

function SmartFieldInner({ type = "text", placeholder = "", encryptKey,
  sfSecurity = "brief", sfStealth, sfType, sfDomain, style = {}, id }) {

  const containerRef = useRef(null);
  const sfRef = useRef(null);
  const mountedRef = useRef(false);

  useEffect(() => {
    if (mountedRef.current && sfRef.current) return;
    mountedRef.current = true;
    const container = containerRef.current;
    if (!container) return;

    function createField() {
      if (sfRef.current && container.contains(sfRef.current)) return;
      const sf = document.createElement("smart-field");
      sf.setAttribute("type", type);
      sf.setAttribute("placeholder", placeholder);
      sf.setAttribute("sf-security", sfSecurity);
      if (sfStealth) sf.setAttribute("sf-stealth", "");
      if (sfType) sf.setAttribute("sf-type", sfType);
      if (sfDomain) sf.setAttribute("sf-domain", sfDomain);
      if (encryptKey) sf.setAttribute("encrypt-key", encryptKey);
      if (id) sf.id = id;
      const s = Object.entries(style).map(([k,v]) => k+":"+v).join(";");
      if (s) sf.setAttribute("style", s);
      container.appendChild(sf);
      sfRef.current = sf;
      if (id) sf.addEventListener("sf-input", () => {
        try { _values.set(id, sf.getRealValue?.() ?? ""); } catch {}
      });
    }

    // CRITICAL: wait for smartfield.js to load before creating element
    if (customElements.get("smart-field")) createField();
    else customElements.whenDefined("smart-field").then(createField);

    return () => {
      if (sfRef.current && container.contains(sfRef.current))
        container.removeChild(sfRef.current);
      if (id) _values.delete(id);
      sfRef.current = null;
      mountedRef.current = false;
    };
  }, []);

  return <div ref={containerRef} />;
}

export const SmartField = memo(SmartFieldInner);

// Read plaintext value (3 fallback levels)
export function getSmartFieldValue(id) {
  const el = document.getElementById(id);
  try { const v = el?.getRealValue?.(); if (v !== undefined) return v; } catch {}
  try { const v = el?._s?.("realValue"); if (v !== undefined) return v; } catch {}
  return _values.get(id) || "";
}

// Read encrypted value (for server-side decryption)
export function getSmartFieldEncrypted(id) {
  const el = document.getElementById(id);
  return el?.value || "";
}

Step 3: Use in your login page

// app/login/page.tsx
"use client";
import { SmartField, getSmartFieldValue } from "@/components/SmartField";

export default function LoginPage() {
  async function handleSubmit(e) {
    e.preventDefault();
    const email = getSmartFieldValue("sf-email");
    const password = getSmartFieldValue("sf-pwd");

    await fetch("/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password })
    });
  }

  return (
    <form onSubmit={handleSubmit}>
      <SmartField id="sf-email" type="email" placeholder="Email"
        sfSecurity="peek" encryptKey="/sf-key" />
      <SmartField id="sf-pwd" type="password" placeholder="Password"
        sfSecurity="peek" encryptKey="/sf-key" />
      <button type="submit">Log In</button>
    </form>
  );
}
Why the wrapper? React re-renders destroy Web Components. The wrapper uses customElements.whenDefined() to wait for the script, memo() to prevent re-renders, and mountedRef to handle React Strict Mode. Without this, SmartField loses all typed data on state changes.

Vue.js

<!-- In your index.html -->
<script src="https://smartfield.dev/component/smartfield.js"></script>

<!-- In your Vue component -->
<template>
  <form @submit.prevent="handleLogin">
    <smart-field type="email" placeholder="Email"
      encrypt-key="/api/sf-key" ref="email"></smart-field>
    <smart-field type="password" placeholder="Password"
      encrypt-key="/api/sf-key" ref="password"></smart-field>
    <button type="submit">Log In</button>
  </form>
</template>

<script>
// Tell Vue to ignore smart-field as a custom element
// In vite.config.js:
// vue({ template: { compilerOptions: { isCustomElement: tag => tag === 'smart-field' } } })
</script>

Angular

// In angular.json, add to scripts:
"scripts": ["https://smartfield.dev/component/smartfield.js"]

// In app.module.ts:
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA]  // allows smart-field tag
})

// In your template:
<smart-field type="password" placeholder="Password"
  encrypt-key="/api/sf-key"></smart-field>

Security Modes (sf-security)

SmartField has 3 display modes for how typed characters appear:

<!-- MAX (default): All characters are cipher symbols. Never reveals real text. -->
<smart-field sf-security="max"></smart-field>
<!-- User types "hello" → screen shows "ΣΩΔψξ" -->

<!-- PEEK: Hold the eye button to reveal text for 5 seconds -->
<smart-field sf-security="peek"></smart-field>
<!-- User holds 👁 button → shows "hello" for 5s with countdown bar -->

<!-- BRIEF: Shows each character for 1 second, then replaces with cipher -->
<smart-field sf-security="brief"></smart-field>
<!-- User types "h" → shows "h" for 1s → becomes "Σ" -->
Recommended: Use peek for login forms (users can verify their input). Use max for payment fields (maximum security). Use brief for general forms.

Stealth Mode (sf-stealth)

Hides the field's purpose from bots that read HTML source:

<!-- Without stealth: bots see placeholder="password" in HTML source -->
<smart-field type="password" placeholder="password"></smart-field>

<!-- With stealth: placeholder shows cipher chars, reveals real text on focus for 2s -->
<smart-field type="password" placeholder="password" sf-stealth></smart-field>
<!-- HTML source shows: placeholder="ΣΩΔψξ" -->
<!-- On focus: shows "password" for 2s, then fades to cipher -->

Field Types (sf-type)

<!-- Credit card: 16 digits only, blocks letters -->
<smart-field sf-type="card"></smart-field>

<!-- Expiry: MM/YY auto-format, validates month 01-12 -->
<smart-field sf-type="expiry"></smart-field>

<!-- CVV: 3-4 digits only -->
<smart-field sf-type="cvv"></smart-field>

<!-- SSN: 9 digits only -->
<smart-field sf-type="ssn"></smart-field>

<!-- Phone: 10-15 digits -->
<smart-field sf-type="phone"></smart-field>

<!-- Password: any text -->
<smart-field type="password"></smart-field>

<!-- Free text: no restrictions -->
<smart-field></smart-field>
Important: sf-type only controls INPUT VALIDATION (what characters are accepted). It does NOT expose the field type to JavaScript. An attacker cannot read sf-type from the DOM. it's consumed during initialization and not stored as a readable attribute.

Custom Styling

/* SmartField uses CSS Custom Properties */
smart-field {
  --sf-bg: #ffffff;           /* background */
  --sf-border-color: #e5e7eb; /* border */
  --sf-radius: 10px;          /* border radius */
  --sf-focus-color: #00B88A;  /* focus border color */
  --sf-focus-ring: rgba(0,184,138,0.12); /* focus glow */
  --sf-cipher-color: #00B88A; /* encrypted character color */
  --sf-cipher-glow: rgba(0,184,138,0.3); /* character glow */
  --sf-placeholder-color: #bbb;
  --sf-font-size: 14px;
  --sf-padding: 12px 16px;
}

Security Architecture

ENCRYPTION: Hybrid AES-256-GCM + RSA-OAEP-2048

User types → AES key generated (random, per encryption)
           → Data encrypted with AES-256-GCM
           → AES key encrypted with server's RSA-2048 public key
           → Payload: base64(JSON{version, iv, encryptedKey, encryptedData})
           → Sent to server
           → Server decrypts AES key with RSA private key
           → Server decrypts data with AES key
           → Plaintext available ONLY on server

What SmartField blocks (20/20 verified)

1.  .value                → returns encrypted payload
2.  .shadowRoot            → returns null (closed)
3.  .innerHTML             → returns ""
4.  .textContent           → returns ""
5.  .innerText             → returns ""
6.  querySelector("input") → returns null
7.  querySelectorAll("*")  → returns []
8.  children / childNodes  → empty
9.  outerHTML              → no plaintext value
10. .type / .name / .length→ hidden (encrypted / random / -1)
11. .value = "inject"      → blocked
12. keydown event listener → events don't propagate
13. Prototype pollution    → returns encrypted
14. MutationObserver       → can't observe closed Shadow DOM
15. getComputedStyle       → no data in CSS
16. Input overlay attack   → keystrokes go to Shadow DOM
17. document.execCommand   → blocked by Shadow DOM
18. JSON.stringify         → sensitive data in WeakMap (invisible)
19. Property enumeration   → no plaintext properties
20. Accessibility APIs     → no data in ARIA

Troubleshooting by Stack

SmartField has been tested end-to-end with Node.js, Python, Java, Go, PHP, and Ruby. All SDKs decrypt correctly. Below are real issues we encountered during integration testing and how to fix them.

All Stacks. Field Limit (Free Plan)

Symptom: SmartField fields appear disabled or unresponsive after the 3rd field on the page.

The free plan allows 3 SmartFields per page. Fields beyond the limit are automatically blocked. Fix:

All Stacks. Peek Mode Text Invisible on Dark Backgrounds

Symptom: When holding the shield icon in peek mode, the revealed text is invisible (black text on dark background).

SmartField auto-detects background brightness and adjusts the peek text color. If auto-detection fails (e.g., transparent backgrounds), set the color explicitly:

smart-field {
  --sf-peek-color: #ffffff;  /* white text for dark backgrounds */
}

All Stacks. Wrong Attribute Name

Symptom: Security modes don't work. Field behaves like max mode regardless of setting.

The correct attribute is sf-security, not security:

<!-- ❌ Wrong -->
<smart-field security="peek" ...>

<!-- ✅ Correct -->
<smart-field sf-security="peek" ...>

PHP. JSON Response Contaminated with Print Output

Symptom: Browser shows Unexpected token 'S' or similar JSON parse error. The PHP server returns text before the JSON payload.

Any echo, print, or var_dump before your JSON response will break the output. This includes initialization messages from the SmartField SDK.

// ❌ This breaks JSON responses. output goes to browser
echo "[SmartField] Keys loaded";

// ✅ Use error_log instead. output goes to server logs only
error_log("[SmartField] Keys loaded");
Tip: In PHP's built-in server (php -S), anything printed outside a response handler also contaminates the output. Always use error_log() for debug messages.

Ruby (WEBrick). CORS Preflight Fails

Symptom: Failed to fetch error when SmartField tries to get the public key or submit data cross-origin. Works with curl but fails in the browser.

WEBrick intercepts OPTIONS requests before they reach your route handler, so CORS headers are never sent. Use RequestCallback to inject CORS headers globally:

server = WEBrick::HTTPServer.new(
  Port: 9999,
  RequestCallback: proc { |req, res|
    res['Access-Control-Allow-Origin'] = '*'
    res['Access-Control-Allow-Headers'] = 'Content-Type'
    res['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
  }
)
Note: This applies to WEBrick specifically. If you use Sinatra or Rails, use the rack-cors gem instead, which handles preflight correctly.

Java (HttpServer). Cross-Origin Blocked by Browser

Symptom: Failed to fetch when SmartField component is served from a different origin than the Java server. CORS headers are present but browser still blocks.

Some browsers enforce stricter CORS rules for certain header combinations. The simplest fix is to proxy requests through your frontend server so everything is same-origin:

// Express proxy example (frontend server)
app.all('/proxy/java/:path', (req, res) => {
  const http = require('http');
  const options = {
    hostname: 'localhost', port: 6666,
    path: '/' + req.params.path,
    method: req.method,
    headers: { 'Content-Type': 'application/json' }
  };
  const proxy = http.request(options, (proxyRes) => {
    let data = '';
    proxyRes.on('data', c => data += c);
    proxyRes.on('end', () => res.type('json').send(data));
  });
  if (req.body) proxy.write(JSON.stringify(req.body));
  proxy.end();
});

// SmartField uses the proxy URL
<smart-field encrypt-key="/proxy/java/sf-key">
Alternative: In production, serve your frontend and Java API from the same domain (reverse proxy via nginx/Apache), which eliminates CORS entirely.

Go, Python, Node.js. No Issues

These stacks worked correctly out of the box with standard CORS headers. No special configuration needed beyond the basic setup documented above.

Mobile Devices. Standard Input Mode

Not a bug. On mobile devices, SmartField intentionally renders as a standard HTML input without encryption.

Mobile browsers do not support browser extensions, which eliminates the primary attack vector SmartField protects against:

SmartField detects mobile devices automatically. No configuration needed. The <smart-field> tag still works. it just renders as a normal input on mobile and an encrypted field on desktop.

General. CORS Checklist

If you're getting Failed to fetch errors, verify your server returns these headers on both OPTIONS and regular responses:

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS

Test with curl to confirm:

# Test preflight
curl -X OPTIONS http://localhost:PORT/sf-key \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: GET" \
  -H "Access-Control-Request-Headers: Content-Type" -D -

# You should see all 3 Access-Control headers in the response

What SmartField Protects. And What It Doesn't

We believe in transparency. No security product is 100%. Here's exactly what SmartField covers and what it doesn't.

SmartField PROTECTS against:

AttackHow
JavaScript reading .valueReturns encrypted payload only
Trackers (Hotjar, GA, FullStory)Shadow DOM + event blocking
XSS attacksAttacker gets encrypted gibberish
Screen recordersCipher characters + scramble on blur
Bots and AI agentsShadow DOM blocks querySelector
Copy / paste / selectAll clipboard events blocked
Prototype pollutionvalue property is non-configurable
JSON serializationSensitive data in WeakMap (invisible)
MutationObserverCannot observe closed Shadow DOM
Browser autofill / autosaveInput shows cipher chars, not real data

SmartField DOES NOT protect against:

AttackWhy
Malicious browser extensionsExtensions with elevated permissions operate below the JavaScript layer. SmartField cannot block them. This is a browser-level limitation, not a SmartField limitation.
OS-level keyloggersA keylogger installed on the operating system captures keystrokes before they reach the browser. No web technology can prevent this.
Physical access to the machineSomeone with physical access can install keyloggers, use hardware devices, or read the screen directly. This requires endpoint security, not web security.
Browser zero-day exploitsA vulnerability in Chrome/Firefox itself could bypass Shadow DOM. If this happens, the entire browser is compromised. not just SmartField.
RAM dump with root accessWith root/admin access to the machine, an attacker could read browser process memory. This requires full system compromise.
Our position: SmartField protects the browser layer. the space between the keyboard and the server. This is the layer that no one else protects. We are transparent about what falls outside our scope because we believe honesty builds more trust than false promises.

API Reference. SDK Methods

Server SDK (Node.js)

const sf = require('@smartfield-dev/server');

await sf.init()                    // Generate or load RSA keys
sf.getPublicKey()                  // Returns JWK public key
await sf.decrypt(payload)          // Decrypt a single field
await sf.decryptFields(body)       // Decrypt all fields in an object
sf.middleware()                    // Express middleware (auto key + decrypt)
await sf.rotateKeys()              // Rotate RSA keys (archives old ones)
sf.status()                        // Returns SDK state info

Client-side (Browser). v2.7+

const el = document.getElementById('sf-email');

el.value                // Returns ENCRYPTED payload (for server decryption)
el.getRealValue()       // Returns PLAINTEXT value (for client-side use)
el.hasValue()           // Returns true/false (without revealing length)
el.clear()              // Clears the field programmatically

// All methods above are non-enumerable and non-configurable:
// - Invisible to JSON.stringify, Object.keys, for..in
// - Cannot be overridden by attackers
When to use .value vs .getRealValue():

.value → Send to your server for decryption (end-to-end encrypted flow)
.getRealValue() → Use client-side when you need plaintext (e.g. validation, sending to your own API)

Use .getRealValue() for React/Vue/Angular apps that handle form submission in JavaScript.

Events

// sf-input: fires on every keystroke
document.querySelector('smart-field').addEventListener('sf-input', (e) => {
  console.log(e.detail.id);         // random field ID (sf_a7f3c2)
  console.log(e.detail.encrypted);  // encrypted payload
  // NOTE: no plaintext, no field name, no length exposed
});

// sf-scan: fires after environment scan (500ms after mount)
el.addEventListener('sf-scan', (e) => {
  console.log(e.detail.threats);    // array of detected threats
  console.log(e.detail.safe);       // true if no threats found
});

// sf-threat: fires if phishing detected on form submit
el.addEventListener('sf-threat', (e) => {
  console.log(e.detail.threats);    // what was detected
  console.log(e.detail.action);     // 'fake_data_sent'
});

Compliance

SmartField's encryption architecture can help satisfy specific requirements in PCI-DSS, HIPAA, GDPR, and SOX. Below is an honest breakdown: what SmartField covers, and what you still need to handle yourself.

SmartField is not a compliance certification. Using SmartField does not make you PCI, HIPAA, GDPR, or SOX compliant on its own. Compliance requires organizational policies, server-side controls, access management, auditing, and more. SmartField addresses the client-side data capture layer. one piece of a larger compliance program.

PCI-DSS

The Payment Card Industry Data Security Standard applies to any organization that stores, processes, or transmits cardholder data. SmartField directly addresses three requirements:

RequirementWhat it saysHow SmartField helps
3.4. Render PAN unreadable anywhere it is stored PAN must be protected using encryption, truncation, hashing, or tokenization SmartField encrypts PAN at the moment of keystroke using AES-256-GCM. The value never exists as plaintext in the DOM, JavaScript memory, or network payload. With sf-type="card", input is validated to 16 digits before encryption. The browser never holds the raw PAN in a readable form.
4.1. Encrypt cardholder data in transit over open networks Use strong cryptography when transmitting cardholder data Data is encrypted client-side with AES-256-GCM before form submission. The AES key itself is wrapped with RSA-OAEP-2048. This means cardholder data is encrypted before it hits TLS. providing a second encryption layer. Even if TLS is compromised (MITM, misconfigured proxy), the payload remains encrypted.
6.5. Address common coding vulnerabilities Protect against XSS, injection, and other OWASP Top 10 issues in web applications SmartField's closed Shadow DOM prevents XSS attacks from reading field values. Even if an attacker injects a script, .value returns an encrypted payload, .shadowRoot returns null, and no DOM traversal method exposes plaintext. Clipboard events (copy/paste/select) are blocked.

What you still need to do:

Scope reduction: Because cardholder data is never exposed as plaintext in the browser, SmartField can help reduce the scope of your PCI-DSS assessment for the client-side environment. Your frontend code never touches raw PAN, which simplifies SAQ questionnaires.

HIPAA

The Health Insurance Portability and Accountability Act requires protection of PHI (Protected Health Information). names, dates, SSNs, medical record numbers, and any data that can identify a patient.

SafeguardHIPAA requirementHow SmartField helps
Technical Safeguard §164.312(a)(1). Access Control Implement technical policies to allow access only to authorized persons PHI entered in SmartField is immediately encrypted. JavaScript on the page (including third-party scripts, analytics, and injected code) cannot read the plaintext. Only your server with the RSA private key can decrypt. This limits access to PHI at the point of capture.
Technical Safeguard §164.312(e)(1). Transmission Security Protect PHI during electronic transmission PHI is encrypted with AES-256-GCM + RSA-2048 before transmission. This operates independently of (and in addition to) TLS. PHI is encrypted in the browser and decrypted only on your server.
Technical Safeguard §164.312(a)(2)(iv). Encryption and Decryption Encrypt ePHI as appropriate SmartField encrypts all field data using NIST-recommended algorithms (AES-256-GCM, RSA-OAEP). Encryption happens automatically. developers cannot accidentally skip it.

What you still need to do:

Important: SmartField never sees, stores, or transmits PHI. All encryption and decryption happens on your own infrastructure using keys generated on your server. This means SmartField itself is not a HIPAA Business Associate. but your server-side code that handles decrypted PHI must comply fully.

GDPR

The General Data Protection Regulation requires protection of personal data for EU residents. SmartField addresses several GDPR principles at the data capture layer.

PrincipleGDPR articleHow SmartField helps
Data Minimization Article 5(1)(c) SmartField's random field IDs (sf_a7f3c2) mean that even the encrypted payload does not reveal what type of data was collected. There are no field names like "email" or "password" in the submitted form data. Third-party scripts on the page cannot determine what data the form collects.
Integrity & Confidentiality Article 5(1)(f) Personal data is encrypted at the earliest possible point. the moment of keystroke. AES-256-GCM provides both confidentiality (encryption) and integrity (authentication tag). Data cannot be tampered with or read between capture and server-side decryption.
Data Protection by Design Article 25 SmartField enforces encryption by default. There is no "unencrypted mode." Developers cannot accidentally expose personal data because .value always returns the encrypted payload. This is protection by design, not by policy.
Security of Processing Article 32 SmartField implements encryption, pseudonymization (random field IDs), and protection against unauthorized disclosure (closed Shadow DOM blocks third-party scripts). These are specific measures listed in Article 32(1)(a).

What you still need to do:

Third-party script protection: A major GDPR risk is third-party JavaScript (analytics, chat widgets, A/B testing tools) silently reading form data. SmartField eliminates this risk entirely. these scripts see only encrypted payloads and random field IDs.

SOX (Sarbanes-Oxley)

SOX Section 404 requires internal controls over financial reporting. For web applications that handle financial data (accounting portals, expense systems, internal financial tools), SmartField provides controls at the input layer.

Control areaSOX requirementHow SmartField helps
IT General Controls. Data Integrity Ensure accuracy and completeness of financial data AES-256-GCM includes an authentication tag that detects any modification to the encrypted data. If a script or middlebox alters the payload in transit, decryption fails. This guarantees that financial data received by the server is exactly what the user entered.
IT General Controls. Access to Data Restrict access to financial data to authorized personnel and systems Financial figures entered in SmartField cannot be read by client-side JavaScript, browser extensions at the JS layer, or third-party scripts. Only your server with the private key can decrypt. This restricts access at the point of entry.
IT General Controls. Change Management Ensure changes to applications do not compromise data integrity SmartField's encryption is automatic and non-bypassable. A developer adding new frontend code cannot accidentally expose financial data. the architecture enforces protection regardless of application changes.

What you still need to do:

SOX is about process, not just technology. SmartField provides a verifiable technical control at the input layer (encrypted capture, tamper detection, access restriction). But SOX compliance requires documented procedures, regular testing, and management sign-off across your entire financial reporting infrastructure.

License Keys

<!-- Free plan: no key needed -->
<script src="https://smartfield.dev/component/smartfield.js"></script>
<!-- Limit: 3 fields per page, badge shown -->

<!-- Paid plan: add your key -->
<script src="https://smartfield.dev/component/smartfield.js"
        data-key="sf_live_your_key_here"></script>
<!-- Unlimited fields, no badge -->
License key is tied to your domain. If someone copies your key to another domain, SmartField falls back to the free plan on that domain. Your key only works on the domain you registered.

Frequently Asked Questions

Why doesn't browser autofill work with SmartField?

This is intentional, not a bug.

Browser autofill is a security risk. When Chrome, Firefox, or Safari autofill your login form, the password is stored in the browser and inserted as plaintext into the DOM. This means:

SmartField blocks autofill by design: the input lives inside a closed Shadow DOM with autocomplete="off", password manager ignore flags, and type="text" (not "password"). The browser cannot detect, fill, or save the field.

The trade-off: Users type their password manually every time. Use sf-security="peek" so they can hold the eye icon to verify what they typed. this replaces the convenience of autofill with a secure alternative.

Can I use SmartField alongside normal inputs?

Yes. You don't have to encrypt every field. Use SmartField for sensitive fields (passwords, card numbers, SSN) and normal <input> for non-sensitive fields (username, search, preferences). SmartField will even warn you if it detects unprotected <input type="password"> fields on the same page.

Does SmartField work on mobile?

Yes. SmartField supports both physical keyboards (keydown) and virtual keyboards (beforeinput) on iOS and Android. The cipher animation, peek mode, and encryption all work on mobile browsers.

What happens if the SmartField server goes down?

If you're using the free plan (no data-key), SmartField works 100% offline. it generates encryption keys locally in the browser. No server call needed.

If you're using a paid plan, SmartField caches the license in sessionStorage for 24 hours. If the license server is unreachable, it falls back gracefully to the free plan (3 fields, badge shown). Your forms never break.

Can attackers decrypt the data if they steal my public key?

No. The public key is public by design. it's served to every browser that loads your page. It can only encrypt, never decrypt. Only your server's private key (stored in .smartfield/) can decrypt. This is standard RSA asymmetric cryptography.

Is my API key (data-key) safe in my HTML?

Yes. Your API key is designed to be public, just like a Google Maps key or Stripe publishable key. It goes directly in your HTML:

<script src="smartfield.js" data-key="sf_live_xxx"></script>

You do NOT need to put it in a .env file or hide it. Here's why it's safe:

  1. Domain-locked: Your key is tied to your domain. If someone copies it and uses it on hacker.com, the server rejects it and falls back to the free plan. It only works on the domain you registered.
  2. No sensitive access: The key only tells SmartField "this site has a Pro plan." It doesn't grant access to any data, keys, or user information.
  3. Cannot decrypt data: Decryption requires your server's RSA private key (stored in .smartfield/private.json). The API key has nothing to do with encryption or decryption.
What you must NEVER expose:
  • Your RSA private key (.smartfield/private.json) — this decrypts user data
  • Your DATABASE_URL, database passwords, or server credentials
  • Your .licenses/ directory
These files are auto-added to .gitignore by the SDK. Never commit them.
In short: data-key = public (like Google Maps key). .smartfield/private.json = secret (like your database password). Never mix them up.

Why do field names show as random IDs (sf_a7f3c2)?

To prevent attackers from identifying which field contains the password. If the field name was "password", an attacker could target that specific field. Random IDs make all SmartField fields look identical. an attacker cannot distinguish the email from the password from the SSN.

Does SmartField slow down my page?

The SmartField script is ~15KB. Encryption uses the browser's native Web Crypto API (hardware-accelerated on most devices). Each keystroke encryption takes <1ms. There is no noticeable performance impact.