// Reads a profile back. The store middleware DECRYPTS the `.encrypted()` column // on read, so the handler holds `ssn` as plaintext and never touches the // `enc:v1:…` ciphertext — that is the round-trip this template exists to show. // // What crosses the WIRE is a SEPARATE decision, and the schema makes it: `ssn` // is also `.serverOnly()`, so the full value may not leave the server. The // executor proves the decryption worked by returning the last four digits it // derived from the plaintext — which is what a support UI would actually // render. Encryption at rest is not an exposure marker; `.serverOnly()` is. import { defineAction } from '@voltro/protocol' import { Schema } from 'effect' export const getProfile = defineAction({ name: 'profiles.get', // Open, and the reason is a claim you can check against the two files beside // this one: the full `ssn` is `.serverOnly()` in `database/schema.ts` and is // not named in the output below, so the sensitive value cannot cross the wire // at all. What is left — name, email, four digits — is the row this demo's // own `profiles.create` put there; the template ships no seed and no other // writer. // // A `guards: [{ scope: 'profiles:read' }]` is what a real deployment wants // here, and it is unsatisfiable in THIS app: no auth strategy and no rbac are // configured, so every caller resolves to an anonymous Subject holding no // scopes and the guard would deny 100% of traffic — an outage, not security. // Add an identity (see `api-auth` / `api-rbac`), then the guard. openAccess: 'returns name, email and the last four digits derived server-side from the decrypted ' + '`ssn`; the full value is `.serverOnly()` and is absent from this output, so it cannot ' + 'cross the wire. Rows come only from this demo\'s own `profiles.create` — no seed, no ' + 'other writer.', input: Schema.Struct({ id: Schema.String }), output: Schema.NullOr(Schema.Struct({ id: Schema.String, name: Schema.String, email: Schema.String, // Proof the cipher decrypted the column, without publishing it. Empty when // the stored value is shorter than four characters. ssnLast4: Schema.String, })), })