Security

Your AWS account stays yours. Here is exactly what we can touch.

Last updated 3 October 2026

Using Ops-Free means letting it send email through your Amazon SES account. That is a real decision, so this page lists the permissions we ask for, what we store, and how to take it all back, including the parts we haven't done yet.

The short version

  • Your mail is sent by your AWS account, under your own agreement with Amazon, and Amazon bills you directly. Your sending reputation is yours alone.
  • One-click connect stores no secret. You create one IAM role. There is no password or access key for us to keep, lose or leak.
  • The role can only do SES things. It can't read your files, launch servers, change your other IAM settings or touch your databases. The full list is below.
  • Delete one CloudFormation stack and our access is gone, within about 15 minutes.

What one-click connect creates

Pressing Connect AWS opens your AWS console on a pre-filled CloudFormation page. You tick one box and press Create stack. The stack creates a single IAM role named OpsFreeSES-… in your account. You can read the whole template before you do.

  • Who can use the role. Its trust policy names one identity, Ops-Free's platform user, and no one else.
  • An ExternalId only you and we know. The role only works when we present the random ExternalId generated for your account. This is AWS's standard defence against a “confused deputy”: another Ops-Free customer can't trick us into using your role, because they don't have your ExternalId.
  • Short-lived access. We get temporary credentials through AWS STS that last 15 minutes and are refreshed as needed. Nothing long-lived is kept.
  • Verified before it counts. We don't trust the stack's message that it finished. We actually assume the role and read your SES account before marking you connected.

Exactly what we can do

The role carries these permissions and no others. The same list appears in the setup guide, and the code reads it from one place, so the page can't drift from what the product does.

Send email and manage identities

Sends your mail, checks whether your account is still in the SES sandbox, and adds and checks your domains and sender addresses.

ses:GetAccountses:SendEmailses:CreateEmailIdentityses:GetEmailIdentity

Delivery events

Creates one configuration set and one SNS topic in your account so bounces, complaints and deliveries are reported back to us, which is how suppression works.

ses:CreateConfigurationSetses:CreateConfigurationSetEventDestinationses:UpdateConfigurationSetEventDestinationses:PutEmailIdentityConfigurationSetAttributessns:CreateTopicsns:Subscribe

Route 53 (optional)

Only if your domain's DNS is in the same AWS account: adds the DKIM records for the domain you're setting up, so you don't paste them yourself. The code does nothing else with it.

route53:ListHostedZonesByNameroute53:ChangeResourceRecordSets

Be aware: these permissions apply to Resource: *, meaning every SES identity and, for the optional Route 53 permission, every hosted zone in the account. The role can send as any identity you have verified in that region, which is what a sending service has to be able to do. If you want a tighter boundary, use a separate AWS account for sending.

What it can't do: read or change anything outside SES, SNS topic creation and the optional DNS records. No S3, EC2, databases, billing or IAM.

The role's permissions (JSON)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ses:GetAccount",
        "ses:SendEmail",
        "ses:CreateEmailIdentity",
        "ses:GetEmailIdentity",
        "ses:CreateConfigurationSet",
        "ses:CreateConfigurationSetEventDestination",
        "ses:UpdateConfigurationSetEventDestination",
        "ses:PutEmailIdentityConfigurationSetAttributes",
        "sns:CreateTopic",
        "sns:Subscribe"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ListHostedZonesByName",
        "route53:ChangeResourceRecordSets"
      ],
      "Resource": "*"
    }
  ]
}

Revoking our access

In the AWS console, open CloudFormation and delete the stack whose name starts with the connection you created. That deletes the role. Ops-Free is told, marks the connection as removed, and even a session we had cached stops working within 15 minutes. You can also delete the role directly in IAM, or tell us to delete your account (see our Privacy Policy).

Revoking never touches your SES identities, domains or sending history in your own account. Those stay with you.

What we store

  • One-click connection: the role's name (ARN), its region and the random ExternalId. No secret.
  • Access-key connection (the “Advanced” option): if you choose it, the secret key is encrypted with AES-256-GCM before storage, bound to your account so it can't be moved to another customer's row, and never shown again. We recommend dedicated keys with only the permissions above. The one-click role is the safer choice.
  • API keys: only a one-way hash and a short display prefix. We can't show a key again. If you lose it, create a new one.
  • Message content: not stored in our database. A message body sits in our queue until it is sent and is removed about 24 hours later. A permanently failed message is kept up to 14 days so you can retry it. We do keep records of each message (sender, recipient, subject, status) so the dashboard can show you what happened.
  • Logs: use an allow-list of fields. Tests check that secrets and message bodies don't appear in them.

The encryption master key can be rotated without asking you to re-enter anything. The full detail is in the Privacy Policy.

Keeping customers apart

  • Every database query is scoped to the signed-in account, and no route takes an account ID from the request.
  • Row-level security is turned on for every table and the public database API is closed. A test fails the build if a new table is missing it.
  • Automated tests act as one customer and check that nothing of another customer's can be read or changed.
  • Delivery-event webhooks are signature-verified before any work is done, and the endpoints are rate limited.

Our own access, and why it's narrow

Ops-Free has one AWS identity of its own, used only to assume customers' roles. Its only permission is sts:AssumeRole on roles named OpsFreeSES-*, and every role also demands that customer's own ExternalId. If that identity's key were ever leaked, we would rotate it without you having to reconnect, and the key alone couldn't assume a role without the ExternalId.

Where it runs

The database and website run in Mumbai, the sending worker in Singapore, and the queue in Mumbai. Error reports go to a monitoring service in the United States, with email addresses and key-like strings removed. The full list of providers is in the Privacy Policy. Traffic uses HTTPS, and the site sets security headers.

What we don't have yet

We would rather you know now than assume:

  • No SOC 2 or ISO 27001 certification. We're a small team. If you need one for procurement, Ops-Free isn't ready for you yet.
  • No independent penetration test yet. We have done our own full security review of the code, the database and the dependencies, and we keep automated tests that guard each fix. That is not the same as an outside audit.
  • One-click connect is new. It has been proven end to end against real AWS using a single account. The delete-the-stack revoke path is covered by our tests but we haven't yet confirmed it on a second, separate AWS account.

Reporting a problem

If you find a security problem, email support@opsfree.in with “Security” in the subject. Please give us a reasonable chance to fix it before you share it publicly, and don't access other customers' data. We will reply, tell you what we find, and credit you if you want.

See also our Terms of Service and Privacy Policy.