{"id":42685472,"url":"https://github.com/ln80/pii","last_synced_at":"2026-01-29T12:16:49.235Z","repository":{"id":37896156,"uuid":"494576796","full_name":"ln80/pii","owner":"ln80","description":"Go library to protect Personal Data at the struct field level","archived":false,"fork":false,"pushed_at":"2024-09-17T10:44:49.000Z","size":157,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-09-18T11:30:38.490Z","etag":null,"topics":["aws-serverless","crypto-shredding","dynamodb","gdpr","go","kms","pii"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ln80.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-20T18:56:45.000Z","updated_at":"2024-09-17T10:44:53.000Z","dependencies_parsed_at":"2024-06-21T14:25:19.809Z","dependency_job_id":"2acc9a60-9650-4e50-a186-1d4f4994bf8d","html_url":"https://github.com/ln80/pii","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ln80/pii","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ln80%2Fpii","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ln80%2Fpii/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ln80%2Fpii/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ln80%2Fpii/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ln80","download_url":"https://codeload.github.com/ln80/pii/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ln80%2Fpii/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28877311,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-serverless","crypto-shredding","dynamodb","gdpr","go","kms","pii"],"created_at":"2026-01-29T12:16:48.427Z","updated_at":"2026-01-29T12:16:49.198Z","avatar_url":"https://github.com/ln80.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"PII\n============\n[![Coverage Status](https://coveralls.io/repos/github/ln80/pii/badge.svg?branch=main)](https://coveralls.io/github/ln80/pii)\n[![GoDoc](https://godoc.org/github.com/ln80/pii?status.svg)](https://godoc.org/github.com/ln80/pii)\n![ci status](https://github.com/ln80/pii/actions/workflows/module.yml/badge.svg)\n![ci status](https://github.com/ln80/pii/actions/workflows/stack.yml/badge.svg)\n\n#### A pluggable Go library to protect [Personal Identifiable Information](https://en.wikipedia.org/wiki/Personal_data) at the struct field level.\n\n#### **TLDR; PII** simplifies encryption and [cryptographic erasure](https://en.wikipedia.org/wiki/Crypto-shredding).\n\n\n## Motivation\n\n**PII** may considerably help if you are:\n\n- Following [Privacy By Design](https://en.wikipedia.org/wiki/Privacy_by_design#Foundational_principles_in_detail) principles.\n- Looking for a solution to comply with privacy standards (ex: GDPR) while using [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) or an immutable store.\n\n## Project Status\n\nThe library is **experimental**; breaking changes may occur based on developer experience.\n\n**`v1.0.0`** aims to be the first stable version.\n\n\n## Getting Started\n\n### Installation:\n\n```shell\n    $ go get github.com/ln80/pii\n```\n```go\n    import \"github.com/ln80/pii\"\n```\n\n### At your struct level:\n\n```go\ntype Person struct {\n    UserID   string `pii:\"subjectID,prefix=account-\"`\n    Fullname string `pii:\"data,replace=forgotten user\"`\n    Gender   string `pii:\"data\"`\n    Country  string\n}\n```\n\n- Tag the field representing the `Subject ID` (ex: UserID)\n- Tag `Personal data` fields to encrypt (only string fields are supported at the moment)\n\n`prefix` option is added to the field value to define the subject ID.\n\n`replace` option is used to replace the crypto-erased field value. Otherwise, the field value will be empty.\n\n\n### At the root level (ex: main func):\n\nInitiate the `Factory` service:\n```go\nfunc main() {\n    ctx := context.Background()\n\n    // newProt func used by factory service to instantiate protector service per namespace\n    newProt := func(namespace string) pii.Protector {\n        // engine handles encryption keys storage and lifecycle\n        engine := memory.NewKeyEngine()\n\n        return pii.NewProtector(namespace, enigne, func(pc *pii.ProtectorConfig) {\n            pc.CacheEnabled = true\n            pc.CacheTTL = 10 * time.Minute\n            pc.GracefulMode = true\n        })\n    }\n\n    // Factory must be injected as dependency in the functional code (ex: HTTP handlers)\n    f := pii.NewFactory(newProt)\n\n    // In a separated Goroutine, supervise and regularly clear resources\n    f.Monitor(ctx)\n}\n```\n\n### At the functional level (ex: HTTP handler):\n\n- Instantiate `Protector` service by passing the namespace (ex: tenant ID)\n- Use the `Protector` to `Encrypt/Decrypt` structs that contain `Personal data`:\n\n```go\nfunc MakeSignupHandler(f pii.Factory, store UserStore) http.HandlerFunc {\n\n        return func(w http.ResponseWriter, r *http.Request) {\n        ctx := r.Context()\n\n        var per Person\n        err := json.NewDecoder(r.Body).Decode(\u0026per)\n        if err != nil {\n            http.Error(w, err.Error(), http.StatusBadRequest)\n            return\n        }\n\n        // Get the protector service for the given namespace\n        nspace := r.Header.Get(\"Tenant-ID\")\n        prot, clear := f.Instance(nspace)\n        \n        // Optional, Force clearing cache of encryption materials (related to namespace)\n        defer clear()\n\n        // Encrypt Person struct which contains PII.\n        if err := prot.Encrypt(ctx, \u0026per); err != nil {\n            http.Error(w, err.Error(), http.StatusBadRequest)\n            return\n        }\n\n        ...\n\n        if err := store.Save(ctx, per); err != nil {\n            http.Error(w, err.Error(), http.StatusBadRequest)\n            return\n        }\n\n        ...\n    }\n}\n\n```\nNote that Factory service maintains a single thread-safe `Protector` service per namespace.\n\nUnder the hood, the `Protector` service generates a single encryption key per `Subject ID` and securely saves it in a `Database`.\n\n\n### Crypto Erasure:\n\nAllows to `Forget` a subject's `Personal data` by first disabling, then deleting the associated encryption materials.\n\n```go\n    ...\n\n    if err := prot.Forget(ctx, subjectID); err != nil {\n        return err\n    }\n\n    ...\n\n    if err := prot.Recover(ctx, subjectID); err != nil {\n        if errors.Is(err, pii.ErrCannotRecoverSubject) {\n            fmt.Print(\"Sorry it's late. Good bye forever\")\n        }\n\n        return err\n    }\n\n```\nForgetting a subject means we can't decrypt nor encrypt any of its old or new `Personal data`.\n\nForgetting the encryption key and not being able to decrypt personal is likely accepted by most `Privacy Standards` as deletion of PII.\nNonetheless, you may need to seek legal advice related to your specific context.\n\nDepending on `Graceful Mode` config, a subject encryption materials can be recovered within a grace period (to define) or not.\n\n\n## Plugins\n\n`Protector` service uses plugins to manage encryption keys and the encryption algorithm.\n\nYou can use your own implementation for each pluggin:\n\n```go\n    b := func(namespace string) pii.Protector {\n        return pii.NewProtector(namespace, nil, func(pc *pii.ProtectorConfig) {\n\n            pc.Encrypter = MyCustomAlgorithm()\n\n            pc.Engine = MyCustomWrapper(\n                MyCustomKeyEngine(),\n            )\n        })\n    }\n\n    f := NewFactory(b)\n```\n\n\n### Encryption algorithm:\nBy default, **PII** uses `AES 256 GCM` for encryption. \n\nBy implementing `core.Encrypter` interface, you take responsibility, and you use your favorite algorithm (likely to respond to security standards requirements).\n\n### Key Engine:\nResponsible for storing encryption keys and managing their life cycle.\n\n**PII** comes with two basic implementations: \n\n- **Dynamodb**: keys are saved in plain text in an [AWS Dynamodb](https://aws.amazon.com/dynamodb/) table. ([server-side encryption](https://docs.aws.amazon.com/dynamodb-encryption-client/latest/devguide/client-server-side.html) can be applied).\n\n- **In-memory**: used for test purposes.\n\n\nand two wrappers:\n\n**KMS Wrapper**: uses [AWS KMS](https://aws.amazon.com/kms/) service to allow [Envelope Encryption](https://docs.aws.amazon.com/wellarchitected/latest/financial-services-industry-lens/use-envelope-encryption-with-customer-master-keys.html); client-side encryption of subjects' keys using a [KMS Key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys) as a `Master Key`.\n\n**Memory Cache**: saves keys in memory for a limited period to enhance performance and reduce costs.\n\nUse your custom logic by implementing `core.KeyEngine`, `core.KeyEngineWrapper` or `core.KeyEngineCache`. \n\n\n## Limitations\n\n// TODO\n\n## Usage and documentation\n\nPlease see https://pkg.go.dev/github.com/ln80/pii for detailed usage docs.\n\n\n## License\n\nDistributed under MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fln80%2Fpii","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fln80%2Fpii","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fln80%2Fpii/lists"}