Security

Your client’s content in Git. Their secrets never.

Your client’s IT lead asks where their Shopify token lives. You open the project and show them. The content is plain files, with a commit for every save. The token sits beside them, out of Git, and nobody can read it back.

every project’s .gitignore

# Written by Moteur in each project's repository
.moteur/
user-data/
.trash/
Credentials, notes and submissions never reach the commits you push.

The project, opened

Where each thing lives

Pick what your client would ask about. Its place lights up, with how it’s kept and who can read it.

Show where it lives

client-site/

  • project.jsonSettings, languages, roles, digests of API keysIn Git
  • models/ pages/ forms/Models, entries, pages, form definitionsIn Git
  • .moteur/secrets.jsonCredentials, encrypted. Readable by the server onlyNever in Git
  • .moteur/Notes, reviews, schedules, the activity logNever in Git
  • user-data/What visitors send through formsNever in Git

On the server, outside any project

  • accountsPeople’s sign-in details, kept by the serverNever in Git
  • environmentThe server’s own keysNever in Git
How it’s kept
Plain files, with a commit for every save. Each one says who changed what, and when.
Who can read it
The project’s people, by role. A site sees only what you chose, and only once it’s live.

Notes, activity and submissions are copied to branches of their own on the server, secrets left out. They never leave with your content.

People

Signing in

Who gets in, and how far they go.

  • Accounts

    Two-step sign-in

    Anyone can turn on an authenticator app. Then a stolen password isn’t enough, even after GitHub or Google.

    • Ten spare codes for a lost phone
    • Sign-in tries are limited

    core/src/twoFactor.ts

    const TICKET_SECONDS = 5 * 60;
    const RECOVERY_CODES = 10;
    
    /** Ten codes like `K7QX-M2PD`, each good once, for when the phone is lost. */
    function newRecoveryCodes(): { shown: string[]; stored: string[] } {
        const shown = Array.from({ length: RECOVERY_CODES }, () => {
            const raw = base32Encode(randomBytes(5)).slice(0, 8);
            return `${raw.slice(0, 4)}-${raw.slice(4)}`;
        });
        return { shown, stored: shown.map(digest) };
    }
  • Roles

    Each person, their part

    The client’s editor writes. Their manager approves. Only your developers change what the site can read.

    • Eight roles, set per project
    • Nobody sees a project they aren’t in

    api/src/middlewares/auth.ts

    export function requireDeveloper(req: Request, res: Response, next: NextFunction) {
        requireProjectAccess(req, res, () => {
            if (!hasRole(req.user, 'developer')) {
                return void res.status(403).json({ error: 'Developer access required' });
            }
            next();
        });
    }
  • Operators

    The people who run the server

    They sign in on a page of their own, with their own authenticator. A user’s session never works there.

    • They can reset a lost phone
    • They can’t read anyone’s codes

Sites and services

What reaches in, and what goes out

  • Keys

    A key that only reads

    The client’s site gets a key for one channel: the models and fields you chose, once they’re live. Lose it, and you make another.

    • Limited to the client’s domains if you like
    • Drafts only through a preview link that expires

    core/src/projectApiKey.ts

    const HASH_ALGORITHM = 'sha256';
    
    function hashKey(rawKey: string): string {
        return crypto.createHash(HASH_ALGORITHM).update(rawKey, 'utf8').digest('hex');
    }
  • Webhooks

    Signed, so you know it’s us

    Every call Moteur makes to your server carries a signature you can check. It never calls an address inside a private network.

    • Tried again if your server is down
    • Its secret is encrypted

    core/src/webhooks/webhookService.ts

    function computeSignature(secret: string, rawBody: string): string {
        const hmac = crypto.createHmac('sha256', secret);
        hmac.update(rawBody);
        return 'sha256=' + hmac.digest('hex');
    }
    
    // …
    'X-Moteur-Event': payload.event,
    'X-Moteur-Delivery': delivery.id,
    'X-Moteur-Signature': signature,
    'X-Moteur-Timestamp': timestamp
  • Forms

    Bots get a thank-you

    And they land in Spam. Your emails and entries only happen for people, and the server checks every answer.

    • Turnstile, hCaptcha or reCAPTCHA
    • Too many sends to one form are refused

    core/src/formGuard.ts

    export async function screenSubmission(
        projectId: string,
        form: FormSchema,
        proof: SubmissionProof,
        locale?: string,
        now = Date.now()
    ): Promise<Screening> {
        if (proof.honeypotTriggered && form.honeypot !== false) return { spamReason: 'honeypot' };
    
        const out: Screening = {};
        const age = formTokenAge(projectId, form.id, proof.token, now);
        if (age !== undefined) out.fillSeconds = age;
        const trap = form.timeTrap;
        if (age !== undefined && age < (trap?.minSeconds ?? DEFAULT_MIN_SECONDS))
            return { ...out, spamReason: 'too_fast' };
        // … then the captcha, asked of its provider

On Moteur Cloud, or on your own server

The engine is the same. What changes is who looks after the machine.

Moteur CloudYour own server
The server’s keysWe keep themYou keep them, and back them up. Lose the encryption key, and the credentials are gone
UpdatesWe install themYou install new releases
HTTPS and the networkOursYours, in front of the API
BackupsAsk us how it’s doneYours: the repositories, .moteur/ and user-data/
Who can read the contentYour project’s people, by roleThe same, and whoever runs the machine
Signing in with Google or MicrosoftTalk to usTurn on the sign-in plugins

Checked 2026-10-27. Self-hosting covers the engine and its API. The Studio runs on Moteur Cloud.

Questions about security

An agency owner asksWhere does Moteur Cloud keep our data?

MoteurAsk us, and we’ll tell you where your projects run and how they’re backed up. We’ll put the answer here once it’s written down for everyone.

A client’s IT lead asksDo you have SOC 2 or ISO 27001?

MoteurNo. If a client needs it, tell us. Until then, this page and the open-source engine are how you check what we do.

An agency owner asksCan a person ask us to delete what they sent through a form?

MoteurYes. Find their messages in the Inbox and delete them. Or add the form data retention plugin: it finds and erases everything one person sent, and lets each form keep letters only as long as you choose.

An agency owner asksCan an AI agent change a client’s site on its own?

MoteurNot unless the project allows it. An agent works with one person’s permissions, and what it writes waits for a reviewer like anyone else’s.

See where everything lives in your own project.

Start for free, save something, and open the repository.