{"id":33204618,"url":"https://github.com/scalapenos/stamina","last_synced_at":"2025-12-17T13:54:44.387Z","repository":{"id":24968818,"uuid":"28387011","full_name":"scalapenos/stamina","owner":"scalapenos","description":"Schema evolution for akka-persistence","archived":false,"fork":false,"pushed_at":"2020-08-18T10:34:21.000Z","size":266,"stargazers_count":145,"open_issues_count":17,"forks_count":30,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-12-10T23:00:10.613Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Scala","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/scalapenos.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}},"created_at":"2014-12-23T08:32:54.000Z","updated_at":"2025-05-14T02:39:00.000Z","dependencies_parsed_at":"2022-08-23T12:11:46.179Z","dependency_job_id":null,"html_url":"https://github.com/scalapenos/stamina","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/scalapenos/stamina","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalapenos%2Fstamina","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalapenos%2Fstamina/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalapenos%2Fstamina/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalapenos%2Fstamina/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scalapenos","download_url":"https://codeload.github.com/scalapenos/stamina/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalapenos%2Fstamina/sbom","scorecard":{"id":803256,"data":{"date":"2025-08-11","repo":{"name":"github.com/scalapenos/stamina","commit":"6b3f9190be80334f13672281bca88b86312f82fb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":4,"reason":"Found 8/18 approved changesets -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T11:05:17.083Z","repository_id":24968818,"created_at":"2025-08-23T11:05:17.083Z","updated_at":"2025-08-23T11:05:17.083Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27783730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-17T02:00:08.291Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-11-16T10:00:18.614Z","updated_at":"2025-12-17T13:54:44.382Z","avatar_url":"https://github.com/scalapenos.png","language":"Scala","funding_links":[],"categories":["JVM"],"sub_categories":["Scala"],"readme":"\n[![Build Status](https://img.shields.io/travis/scalapenos/stamina.svg)](https://travis-ci.org/scalapenos/stamina)\n[![Latest version](https://index.scala-lang.org/scalapenos/stamina/stamina/latest.svg)](https://index.scala-lang.org/scalapenos/stamina/stamina)\n![License](https://img.shields.io/badge/license-MIT-blue.svg \"License: MIT\")\n\nStamina is an Akka serialization toolkit written specifically for use with Akka Persistence.\nIt has a strong focus on long-term viability of your persisted data so it provides support for **versioning** that data, **auto-migrating** that data at read time to be compatible with your current event and domain classes, and a **testkit** to make sure all older versions of your persisted data are still readable.\n\nDo feel free to browse, comment, create issues, pull requests, etc. If you are interested in using Stamina, please feel welcome to join our chat room\n\n[![Join the chat at https://gitter.im/scalapenos/stamina](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/scalapenos/stamina)\n\nWe are still finishing up the last open ends before we release a public version but in the mean time Stamina (and some of its less fancy predecessors) has been running in production for many months now without any problems.\n\n\n## Current status\nStamina is currently available in pre-release SNAPSHOT form. This means that we don't recommend using Stamina in production since the APIs could still change significantly and break your stuff.\n\n[stamina-core](stamina-core) and [stamina-json](stamina-json) are pretty much ready and we are happy with the API that has evolved out of the many experiments we have done over the last year. We **are** looking at the new Akka 2.4.x event adapters to see whether integration with akka can be improved using them.\n\n[stamina-testkit](stamina-testkit) is also mostly finished. It can be used to generate scalatest tests for your stamina persisters, keeping serialized older versions of your data around to make sure you don't accidentally break compatibility.\n\n## Adding Stamina to your project\nStamina is available via [Maven Central](https://search.maven.org/), simply add it to your SBT build:\n\n```scala\nlibraryDependencies += \"com.scalapenos\" %% \"stamina-json\" % \"0.1.4\"\n```\n\n# Stamina in Detail\nStamina is an Akka serialization toolkit written specifically for use with Akka Persistence. Its main defining characteristics are:\n\n- support for explicitly versioning serialized data. Stamina always stores a version number with the serialized data.\n- support for explicitly migrating serialized data written for older versions to the latest version\n- support for defining (de)serialization behavior in code. This includes (auto-)generating so-called *persisters* from Scala case classes and an API for specifying migration behaviors.\n- decoupling from fully qualified class names (or randomly generated ids) as serialization keys. Instead, Stamina uses simple String keys to identify serialized types.\n- support for multiple serialization libraries as implementation plugins, as long as they can be ported/adjusted/wrapped in order to support the above features.\n\nThe first (and currently only) implementation is based on spray-json. It supports migration from older versions using a very simple little DSL to pre-process the JSON AST based on the specific version being read before deserialization takes place. Here's an example:\n\n```scala\n// This example uses explicitly versioned case classes (i.e. the same\n// domain class in three different versions with three different names)\n// to more easily show how to deal with versions and migrations.\n//\n// Normally, of course, you would only need one case class,\n// which would always represent the current version (V3 in this case).\nimport spray.json.lenses.JsonLenses._\nimport stamina.json._\n\n// For this example, we auto-generated json formats from spray-json-shapeless\n// This is not a requirement and you can use explicit formats too.\nimport fommil.sjs.FamilyFormats._\n\n// spray-json persister for V1.\n// Essentially equivalent to any existing Akka serializer except\n// for the simple API used to specify/generate them.\nval v1CartCreatedPersister = persister[CartCreatedV1](\"cart-created\")\n\n// spray-json persister for V2 but with support for migration\n// of data writen in the V1 format.\nval v2CartCreatedPersister = persister[CartCreatedV2, V2](\n  \"cart-created\",\n  from[V1]\n    .to[V2](_.update('cart / 'items / * / 'price ! set[Int](1000)))\n)\n\n// spray-json persister for V3 but with support for migration\n// of data writen in the V1 and V2 formats.\nval v3CartCreatedPersister = persister[CartCreatedV3, V3](\n  \"cart-created\",\n  from[V1]\n    .to[V2](_.update('cart / 'items / * / 'price ! set[Int](1000)))\n    .to[V3](_.update('timestamp ! set[Long](System.currentTimeMillis)))\n)\n```\n\nFor these persisters to be actually used by the Akka serialization system, you will need to bundle them into an Akka\nserializer and then register that serializer for your classes. To make that registration process a little simpler\nStamina comes with a marker trait called `Persistable`. Of course you can use your own marker traits instead.\n\n```scala\nclass CartCreatedV3(...) extends Persistable\n```\n\nIn the example below we create a subclass of `StaminaAkkaSerializer` and pass all our Persister instances into it. We\nthen register this serializer with Akka in our application.conf and bind it to all instances/subclasses of the\n`Persistable` marker trait.\n\n```scala\nclass WebshopSerializer extends StaminaAkkaSerializer(v3CartCreatedPersister, ...)\n```\n\n```\nakka.actor {\n  serializers {\n    serializer  = \"package.name.WebshopSerializer\"\n  }\n  serialization-bindings {\n    \"stamina.Persistable\" = serializer\n  }\n}\n```\n\nAn example application and a testkit with support for regression\ntesting of serialized older versions are currently being worked on.\n\nSupport for Apache Avro and Scala's own pickling formats is pending.\n\n\n## Why?\nStamina aims to be the serialization toolkit that should have come with Akka Persistence but didn't.\n\n### So what's wrong with Akka Persistence?\nAkka Persistence is an awesome library for implementing event stores and event-sourced persistent actors.\n\nThe problem arises from the fact that Akka Persistence reuses the standard Akka serialization system without adding any kind of support for data versioning or deserialization of older versions of persisted data.\n\nThis has lead to people having to maintain multiple older versions of their domain/event model in parallel or to somehow work around this limitation. This project has grown out of one such workaround.\n\nThe Akka serialization system was originally written to support remote actors and clustered actors. It is optimized for raw performance and low configuration overhead but it assumes that the code that serializes your data is the exact same code that deserializes it later. This is not necessarily the case in an event store, where data might have been serialized by an older iteration of your code.\n\nAn event store without some kind of versioning/migration system is not very useful in our opinion so we set out to provide these features in a way that is backwards compatible with Akka Persistence.\n\nSome other problems with the existing Akka serialization system (and many other serialization libraries) are:\n\n- coupling between the serialized data and a specific Java/Scala class or class name, usually even a fully qualified one. This creates a barrier to refactoring of the domain model, something that is very necessary to keep active code bases healthy and clean. Renaming and repackaging of such classes is no longer possible without losing backwards compatibility with your already persisted versions.\n\n- no high-level API for configuring which classes get serialized how. The only available option is linking the fully qualified class name of a specific class (or superclass) to the fully qualified classname of a serializer. This allows for very little flexibility, no support for versioning or migrations, and leads to having to write lots of explicit, low-level serialization code.\n\nMost of these problems arise from the simple fact that versioning and migration were never part of the design of such serialization systems.\n\n\n## Goals / Approach\n\n1. Stamina should support pluggable serialization implementations so people can reuse their existing formats and libraries.\n2. Stamina should support code-level configuration options like type classes and macros so it can use all the power provided by the existing libraries.\n3. Stamina should provide explicit support for versioning serialized data (i.e. data is always written together with a version number).\n4. Stamina should provide support for decoupling from the fully qualified class name of serialized classes (i.e. data is always written together with a symbolic key),\n5. Stamina should provide an easy to use API for deserializing older versions of stored classes (i.e. migrations).\n6. Stamina should be highly customizable from code while requiring the least possible amount of boilerplate\n7. Stamina should use the Scala type system to prevent common serialization, versioning, and migration problems.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalapenos%2Fstamina","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscalapenos%2Fstamina","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalapenos%2Fstamina/lists"}