AACsearch

Security best practices

[TODO i18n — see issue #76] "Best practices for using AACsearch securely — protect your data, API keys, and account."

Security best practices

Security is a shared responsibility. AACsearch provides industry-grade infrastructure; you must use the account and API keys correctly.

API key management

Never commit keys to Git

CRITICAL: API keys must live in environment variables, NOT in code.

Wrong:

const apiKey = "ss_search_abc123def456";  // ❌ Don't do this!
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

Right:

const apiKey = process.env.AACSEARCH_API_KEY;  // ✓ Use environment variables
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

Create dedicated keys per service

Instead of using a single admin key everywhere, create specialized keys:

KeyScopeWhere to useStep
Public search keysearchClient-side, browser, mobile app1
Ingest keyingestServer-side, when uploading documents2
Admin keyadminOnly on your personal machine, never on a server3

How to create specialized keys

  1. Open Search → API keys
  2. Click Create key
  3. Give it a name (e.g., "Frontend Search" or "Import Worker")
  4. Pick the scope: Search / Ingest / Admin
  5. Pick the indexes the key may access (optional)
  6. Click Create
  7. Copy the key once and save it to .env.local

Note: the key is shown only once at creation. If you lose it, create a new one.

Key rotation

Every 3 months create new keys and delete the old ones:

# Create a new key
# (in AACsearch dashboard → API keys → Create key)

# Update environment variables
echo "AACSEARCH_API_KEY=ss_search_new_key_here" >> .env.local

# Test that the new key works
curl -H "Authorization: Bearer ss_search_new_key_here" \
  https://app.aacsearch.com/api/v1/indexes

# Delete the old key
# (dashboard → API keys → Old key → Delete)

If a key is compromised

If you accidentally pushed a key to a public repo or shared it with someone:

  1. Delete the key immediately — open API keys → Old key → Delete
  2. Create a new key — follow the steps above
  3. Audit access logs — open Settings → Audit and check for unauthorized access
  4. Reindex sensitive data — if it was an admin key, recreate the indexes

Account protection

Use a strong password

Use a password that:

  • ✓ Includes uppercase (A–Z), lowercase (a–z), digits (0–9), and specials (!@#$%^&*)
  • ✓ Is at least 12 characters long (16+ preferred)
  • ✓ Doesn't match your name, email, or company name
  • ✓ Has not been used on other sites

Example of a strong password:

Tr0p1cal!Sunset#2025$AACsearch

Bad password:

aacsearch123        ❌ Too simple
Password!           ❌ Too common

Use a password manager (1Password, Bitwarden, Dashlane) to generate and store passwords.

Enable two-factor authentication (2FA)

2FA makes signing in impossible even if someone learns your password.

  1. Open Settings → Security
  2. Find the Two-factor authentication section
  3. Click Enable 2FA
  4. Pick a method:
    • Authenticator app (recommended) — Google Authenticator, Microsoft Authenticator, Authy
    • SMS (less secure) — code via SMS
  5. Save the recovery codes in a safe place

Where to keep recovery codes:

  • ✓ In a password manager
  • ✓ In an encrypted file on your computer
  • ✓ Printed on paper in a safe
  • ✗ In plain text in the cloud (Google Drive, Dropbox)

Review active sessions

If you notice suspicious activity:

  1. Open Settings → Security → Active sessions
  2. Review the list of devices you signed in from
  3. Click Sign out next to any unknown device

Data protection

Limit team access

  1. Open Settings → Organization members

  2. Create members with different rights:

    • Admin — full access (owner only)
    • Editor — can create and edit indexes
    • Viewer — read-only access to stats and search results
    • Support — can answer questions but not edit data
  3. Remove members who no longer work for the company

Use HTTPS everywhere

Make sure all API requests go through HTTPS, not HTTP:

# ✓ Right
curl https://app.aacsearch.com/api/v1/indexes

# ✗ Wrong
curl http://app.aacsearch.com/api/v1/indexes  # ❌ Data is unencrypted

Log all index actions

If you need an audit of who did what:

  1. Open Settings → Audit
  2. You see every action: who created an index, who uploaded documents, who changed settings
  3. Export the logs for analysis (if required by law)

Backend security

Never search directly from the client

Bad:

// On the client (browser)
const apiKey = "ss_search_...";  // ❌ API key visible in source code
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

Good:

// On the client (browser)
const response = await fetch('https://your-domain.com/api/search', {
  method: 'POST',
  body: JSON.stringify({ query: 'sneakers' })
});

// On the server (Node.js, Python, Go)
const apiKey = process.env.AACSEARCH_API_KEY;  // Key in environment variables
const searchResponse = await fetch('https://app.aacsearch.com/api/v1/search', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

Use environment variables

Right:

# .env.local (do not commit to Git!)
AACSEARCH_API_KEY=ss_search_abc123def456
AACSEARCH_ADMIN_KEY=ss_admin_xyz789uvw012
// code.js
const apiKey = process.env.AACSEARCH_API_KEY;

In GitHub Actions / CI-CD:

# .github/workflows/deploy.yml
- name: Deploy
  env:
    AACSEARCH_API_KEY: ${{ secrets.AACSEARCH_API_KEY }}
  run: npm start

Add server-side rate limiting

If you expose a public search endpoint, protect it from DDoS:

// Express.js with express-rate-limit
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 100  // up to 100 requests per 15 minutes
});

app.post('/api/search', limiter, async (req, res) => {
  // Your search code
});
# Django with django-ratelimit
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='100/15m')  # 100 requests per 15 minutes
def search(request):
    # Your search code

Validate user input

Never pass user input straight to the AACsearch API:

// ❌ Bad
app.post('/search', (req, res) => {
  const query = req.query.q;  // Dangerous!
  fetch(`https://app.aacsearch.com/api/v1/search?query=${query}`, ...);
});

// ✓ Good
const Joi = require('joi');

app.post('/search', (req, res) => {
  const { error, value } = Joi.object({
    query: Joi.string().max(500).required(),
    limit: Joi.number().max(100).default(20)
  }).validate(req.query);

  if (error) {
    return res.status(400).json({ error: error.message });
  }

  fetch(`https://app.aacsearch.com/api/v1/search`, {
    method: 'POST',
    body: JSON.stringify({ query: value.query, limit: value.limit })
  });
});

Frontend security

Use public search keys (read-only)

When embedding the widget on a site, use a public search key that can only read data:

<div id="aac-search"></div>
<script
  src="https://app.aacsearch.com/api/widget/widget.js"
  data-api-key="ss_search_..."  <!-- Public key, safe in the browser -->
  data-index-slug="products"
  data-container="#aac-search">
</script>

A public search key can:

  • ✓ Search the index
  • ✓ Receive results

A public search key CANNOT:

  • ✗ Upload documents
  • ✗ Delete documents
  • ✗ Change configuration
  • ✗ See other API keys

Use Content Security Policy (CSP)

Protect against XSS and script injection:

<!-- In <head>, NOT inside the widget -->
<meta http-equiv="Content-Security-Policy"
      content="script-src 'self' https://app.aacsearch.com; img-src 'self' data: https:;">

This allows scripts only from your domain and aacsearch.com.

Monitor browser requests

Use Developer Tools (F12) to inspect requests:

  1. Open F12 → Network
  2. Make a search in the widget
  3. Look at the /api/v1/search request
  4. Verify:
    • ✓ Uses HTTPS (not HTTP)
    • ✓ Header Authorization: Bearer ss_search_...
    • ✓ Sends only the necessary data (query, filters)
    • ✗ Does NOT send personal user data (IP, email, phone)

Incident response

If you spot unauthorized access

  1. Immediately delete the compromised key — open API keys → Delete
  2. Check logs — open Settings → Audit and look at the date and time of the access
  3. See what was downloaded — which indexes and documents were touched
  4. Contact support — for serious breaches reach out to security@aacsearch.com

If you lost the password

  1. On the sign-in page click Forgot password?
  2. Enter your email
  3. Click the link in the email (valid for 24 hours)
  4. Set a new password
  5. Sign in with the new password

If the email didn't arrive:

If you lost the 2FA key

If you no longer have access to the authenticator app:

  1. On the sign-in page click Lost access to 2FA?
  2. Use one of the recovery codes you saved when enabling 2FA
  3. Set up a new 2FA app
  4. If you don't have recovery codes, email support@aacsearch.com

Standards compliance

SOC 2 Type II

We have completed a SOC 2 Type II audit, which checks:

  • Security — protection from unauthorized access
  • Availability — service is available 99.9% of the time
  • Processing Integrity — data is not lost or corrupted
  • Confidentiality — data stays confidential
  • Privacy — compliance with data protection laws

Security checklist

Before launching in production verify:

  • API keys are stored in .env.local, not in code
  • 2FA is enabled on the account
  • Different keys for different services (search, ingest, admin)
  • HTTPS everywhere (not HTTP)
  • Rate limiting on the server
  • Input validation
  • Audit logs for critical operations
  • Team members have only the rights they need

Help and support

If you have security questions:

Frequently asked questions

Q: Do I need 2FA? A: It is recommended, especially if you have admin access or store sensitive data.

Q: How often should I change the password? A: If it is strong and unique, you don't need to. Only after suspicious activity or compromise.

Q: Can I store the API key in a .env file in Git? A: No. Add .env.local to .gitignore and keep keys only locally or in CI/CD secrets.

Q: What if I accidentally pushed a key to a public repo? A: Delete the key from AACsearch immediately, create a new one, and clean up Git history (use git filter-branch or BFG Repo-Cleaner).

On this page