Skip to content

Eop card browser/m2 eop deck browsing#3181

Open
ayman-art wants to merge 34 commits into
masterfrom
eop-card-browser/m2-eop-deck-browsing
Open

Eop card browser/m2 eop deck browsing#3181
ayman-art wants to merge 34 commits into
masterfrom
eop-card-browser/m2-eop-deck-browsing

Conversation

@ayman-art

@ayman-art ayman-art commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Description

Changes

Feature 1: EoP edition in the Card Browser UI

  • data/website/phrases/en/cards.json: EoP button, header and description

Feature 2: EoP deck and suit registration

Registered eop 5.0/en; suits in source order (SP, TA, RE, ID, DS, EP), starting with Ace like the other editions.

  • src/lib/services/deckService.ts, deckServiceConsts.ts, src/domain/suit/suitController.ts
  • src/domain/suit/order.ts, src/domain/card/order.ts

Feature 3: Card routes for all 78 EoP cards

SP2 is the default card on version switch (as AAI2 for Companion).

  • src/domain/card/cardIds.ts: shared card id allowlist used by all card
    routes
  • src/routes/cards/+page.server.ts, +page.svelte, src/routes/cards/[card]/…
  • src/routes/edition/[edition]/+page.svelte, edition/[edition]/[card]/…
    and its [version]/[lang] children (cards_options)

Feature 4: EoP card styling faithful to the printed deck

The styles were initially ported from copi, then reworked to match the original deck: element
sizes, positions, fonts and colors are derived from the official EoP card
PDF.

  • src/lib/components/cardPreview.svelte: reworked EoP styles to match the
    original design instead of copi:
    • divided the styles into elements (watermark, artwork, number tab, text
      block); the watermark only renders on non-royal cards
    • royal (J/Q/K) backgrounds come from the card images; the CSS color is
      just a loading fallback
    • cqw sizing with the original 413/713 ratio, replacing copi's fixed sizes
      and per-card offsets
    • geometry and font sizes referenced from the original design
    • suit colors sampled from the original design, fixing copi's Repudiation
      tab (dark red → gray) and Information Disclosure tab (yellow → blue)
    • suit name grows upward on wrap, keeping the description fixed as in the
      original design
    • static/images/eop-cards/ (78 PNGs): per-card images, the files are taken from
      copi;

Feature 5: STRIDE mappings component

  • src/lib/components/eopCardMapping.svelte: renders the stride field
  • cardPreview.svelte: mappings refactored via a component map so that future editions will require changing one line.

Feature 6: Card taxonomy

Threat Modeling template from M1, Mappings and Attacks sections, same structure as Companion.

  • src/lib/components/eopCardTaxonomy.svelte, cardFound.svelte (same component-map refactor)
  • data/website/phrases/en/cards.json: two translation keys

Feature 7: Copi cards link to cornucopia card pages

EoP cards in copi games now show a "Need more info?" link that opens the card's
page on cornucopia.owasp.org to help players know more about the card.

  • priv/repo/cornucopia/eop-cards-5.2-en.yaml: new deck version as a copy of the
    5.1 deck with url: "https://cornucopia.owasp.org/cards/<id>" added to all 78
    cards
  • priv/repo/migrations/20260717100557_populate_eop_cards52.exs: imports the
    5.2 deck via the existing add_cards_to_database/2
  • lib/copi_web/live/game_live/show.ex: latest_version("eop") increased to
    "5.2" so game sessions use the new deck (tests updated accordingly)
  • lib/copi_web/controllers/card_html/show.html.heex: shows the link on EoP
    cards that have a url, same as companion

Feature 8: Tests

  • deckService.test.ts, decService.integration.test.ts: EoP registration covered

  • Screenshots:

image image image image image image image image

Notes

  • Ace-first order: all source yamls list the Ace last, but the browser shows Ace first for every edition.
  • four files (elevation-of-privilege-{2,3,4}.png, tampering-2.png)
    had an opaque white background that hid the watermark

Resolved or fixed issue: none (partial progress on #1322)

AI Tool Disclosure

  • My contribution does not include any AI-generated content
  • My contribution includes AI-generated content, as disclosed below:
    • AI Tools: [e.g. GitHub CoPilot, ChatGPT, JetBrains Junie etc.]
    • LLMs and versions: [e.g. GPT-4.1, Claude Haiku 4.5, Gemini 2.5 Pro etc.]
    • Prompts: [Summarize the key prompts or instructions given to the AI tools]

Affirmation

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for browsing and rendering the “Elevation of Privilege” (EoP) deck in the SvelteKit website (new edition constant, deck loading, UI toggles, taxonomy/mapping rendering, and i18n strings).

Changes:

  • Add VERSION_EOP and wire EoP (eop, 5.0) into deck/suit loading and existing browsing routes.
  • Extend card-code validation lists and update ordering/color mappings for new EoP suits/cards.
  • Introduce EoP-specific UI components (taxonomy + mapping) and extend tests to cover the new deck.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
cornucopia.owasp.org/src/routes/edition/[edition]/+page.svelte Adds EoP as a selectable edition and renders its description/suits.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/+page.svelte Extends allowed card IDs to include EoP codes and labels edition name for eop.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/+page.svelte Extends allowed card IDs to include EoP codes and labels edition name for eop.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/[lang]/+page.svelte Extends allowed card IDs to include EoP codes and labels edition name for eop.
cornucopia.owasp.org/src/routes/cards/+page.svelte Adds EoP suits to the cards browser UI, preview selection, and noscript output.
cornucopia.owasp.org/src/routes/cards/+page.server.ts Loads EoP deck cards server-side and merges into the deck map.
cornucopia.owasp.org/src/routes/cards/[card]/+page.svelte Extends allowed card IDs to include EoP codes and labels edition name for eop.
cornucopia.owasp.org/src/routes/cards/[card]/+page.server.ts Loads EoP deck cards server-side for per-card pages.
cornucopia.owasp.org/src/lib/services/decService.integration.test.ts Updates integration expectations for EoP deck presence and total card counts.
cornucopia.owasp.org/src/lib/services/deckServiceConsts.ts Adds VERSION_EOP.
cornucopia.owasp.org/src/lib/services/deckService.ts Registers EoP deck metadata (latest + available decks).
cornucopia.owasp.org/src/lib/services/deckService.test.ts Adds unit tests for EoP edition/version/language and adjusts deck counts.
cornucopia.owasp.org/src/lib/components/eopCardTaxonomy.svelte New: taxonomy display for EoP cards (mappings + attacks).
cornucopia.owasp.org/src/lib/components/eopCardMapping.svelte New: compact STRIDE mapping renderer for EoP cards in previews.
cornucopia.owasp.org/src/lib/components/cardPreview.svelte Refactors mapping rendering to support EoP mapping component.
cornucopia.owasp.org/src/lib/components/cardFound.svelte Refactors taxonomy rendering to support EoP taxonomy component.
cornucopia.owasp.org/src/domain/suit/suitController.ts Adds EoP deck/language metadata for suit discovery.
cornucopia.owasp.org/src/domain/suit/order.ts Adds suit ordering for STRIDE suit names.
cornucopia.owasp.org/src/domain/card/order.ts Adds per-suit ordering for EoP card IDs.
cornucopia.owasp.org/src/domain/card/cardColor.ts Adds color keys for STRIDE suit names.
cornucopia.owasp.org/data/website/phrases/en/cards.json Adds EoP copy + i18n keys for button/heading and EoP taxonomy headings.

Comment thread cornucopia.owasp.org/src/lib/components/cardPreview.svelte
Comment thread cornucopia.owasp.org/src/lib/components/cardFound.svelte
@sydseter

sydseter commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

You have the card texts, and the cards are browsable. No styling is applied, but I guess that is what you are working on next?

@ayman-art
ayman-art marked this pull request as ready for review July 9, 2026 04:24
@sydseter

sydseter commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@ayman-art, this is a great start. I would like you to take this a step further. The css for copi.owasp.org was never really in line with the original graphics of the EoP card game. F.ex. We are lacking the numbers in the background on the cards. Which is evident from looking at https://github.com/adamshostack/eop/blob/master/EoP_Card%20Game%20Images.pdf

You should more faithfully try to reproduce the design, as it was initially conceived for the physical cards.
This will lead to some challenges further down the line which could be solved by the idea you proposed as part of your proposal. E.g: How do you make it easier to customise the design for each game presented by the card browser?
This last point is for later.

@ayman-art

Copy link
Copy Markdown
Collaborator Author

@ayman-art, this is a great start. I would like you to take this a step further. The css for copi.owasp.org was never really in line with the original graphics of the EoP card game. F.ex. We are lacking the numbers in the background on the cards. Which is evident from looking at https://github.com/adamshostack/eop/blob/master/EoP_Card%20Game%20Images.pdf

Thanks @sydseter! I'll rework the EoP card styling in this PR to reproduce the original card design as faithfully as possible, focusing on the card sizing and the background numbers that are missing in copi. I'll also keep the CSS structured to make the later customisation work easier.

end


def update_card_urls(path) do

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intention behind this new migration method?

What are you trying to do?

Tje existing code alteady handles card migrations.
If you need to import the urls you should simply update the version number of the yaml file.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added update_card_urls to fill the urls for cards already in existing database, as the populate_cards method skips existing rows, so only editing the yaml wouldn't update them.

So the standard pattern to follow is adding a new yaml with a higher version: I'll drop the new method and migration, and add the urls in a new eop-cards-5.2-en.yaml.
So should the new version be 5.2 (just above the current highest version "5.1")?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spoke to @ayman-art and they will reuse the existing method and clean this one up. @ayman-art go with 5.2 for this migration.

I do wonder if we are now conflating EoP internal versioning with our version for the cards as they move through different iterations? For now, let's treat this as our internal version (assuming that's not what it was already).


def change do
update_card_urls(Path.join(:code.priv_dir(:copi), "/repo/cornucopia/eop-cards-5.0-en.yaml"))
update_card_urls(Path.join(:code.priv_dir(:copi), "/repo/cornucopia/eop-cards-5.1-en.yaml"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are importing the cards twice. Once with v5.0 and once with v5.1. It is worth considering what is happening here. Pay attention to the version number in the meta data in the card yaml file. Does the meta -> version have the correct version number? Is it higher than tje existing number that has been used previously?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn’t need to import the data twice.

ayman-art and others added 4 commits July 17, 2026 16:06
This included:

- Divided the styles into elements (watermark, artwork, number tab,
  text block); the watermark only renders on non-royal cards
- Royal (J/Q/K) backgrounds come from the card images; the CSS color
  is just a loading fallback
- Used cqw sizing with the original 413/713 ratio, replacing copi's
  fixed sizes and per-card offsets
- Geometry and font sizes referenced from the original design
- Sampled suit colors from the original design, fixing copi's
  Repudiation tab (dark red -> gray) and Information Disclosure tab
  (yellow -> blue)
- Made suit name grow upward on wrap, keeping the description fixed
  as in the original design

Signed-off-by: Ayman Algamal <aymanalgamal22@gmail.com>
end


def update_card_urls(path) do

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spoke to @ayman-art and they will reuse the existing method and clean this one up. @ayman-art go with 5.2 for this migration.

I do wonder if we are now conflating EoP internal versioning with our version for the cards as they move through different iterations? For now, let's treat this as our internal version (assuming that's not what it was already).

Comment thread copi.owasp.org/lib/copi/migrations/card_migration.ex Outdated
ayman-art and others added 2 commits July 17, 2026 19:37
@sydseter

sydseter commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Spoke to @ayman-art and they will reuse the existing method and clean this one up. @ayman-art go with 5.2 for this migration.

I do wonder if we are now conflating EoP internal versioning with our version for the cards as they move through different iterations? For now, let's treat this as our internal version (assuming that's not what it was already).

Until now, we have used the official versioning. That said. I have been trying to help Adam with his project for some time without success. I think it is safe to assume that eop will live on separately here.
There will naturally be some differences, but we should try to keep it as true to the original as possible not to make it confusing. Having our own internal versioning makes sense.

@ayman-art

Copy link
Copy Markdown
Collaborator Author

@rewtd, @sydseter We are close now. That said, there are 4 asset images having a white background instead of transparent (elevation-of-privilege-{2,3,4}.png, tampering-2.png) (See tampering-2 vs tampering 3 below), which hides the watermark. I have several options here. Finding a way to fix those 4 pngs, OR extracting them from the PDF as png, OR handling those cards in css as a workaround. What do you think makes more sense? I won't suggest the last one as we want css to be clean for the later refactoring work.
Noting that in the original design, the shapes are always stacking on watermarks (see information-disclosure-10 stacking below), that is why reversing the current stacking wouldn't be a perfect solution.

image image
image

@ayman-art
ayman-art requested review from rewtd and sydseter July 17, 2026 17:52
@sydseter

Copy link
Copy Markdown
Collaborator

@rewtd, @sydseter We are close now. That said, there are 4 asset images having a white background instead of transparent (elevation-of-privilege-{2,3,4}.png, tampering-2.png) (See tampering-2 vs tampering 3 below), which hides the watermark. I have several options here. Finding a way to fix those 4 pngs, OR extracting them from the PDF as png, OR handling those cards in css as a workaround. What do you think makes more sense? I won't suggest the last one as we want css to be clean for the later refactoring work. Noting that in the original design, the shapes are always stacking on watermarks (see information-disclosure-10 stacking below), that is why reversing the current stacking wouldn't be a perfect solution.

image image image

@ayman-art First of all, fantastic work. I love it. I believe we could implement a way to handle exceptions, which should be treated as an optional rendering capability. How this should be done is not that important. It could be just a combination of using an image as a background with some css to hold it in place to make it easy to add the card, but it should probably be an option, not the default.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be generated by a script?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! I believe a main goal of this project is to provide extensibility and make importing new games into the card browser as easy as possible with minimal additions required. That's why the scaffolding script was probably a good idea. That said, styling was the initial priority for this PR, and yes, some of these additions can be automated as you mentioned. I'll keep this in mind, maybe for a separate PR later, to keep this milestone focused on styling.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is fine by me. As I said, it’s up to Grant and you. For now, this is still a great improvement.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Could this be generated by a script? Not saying that you need to do this right now. This pr is fine as it is. Just food for thought.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This as well. Possible to be rendered by a Python script right?

export class SuitController {

private static readonly decks = [{edition: 'mobileapp', version: '1.1'}, {edition: 'webapp', version: '3.0'}, {edition: 'companion', version: '1.0'}];
private static readonly decks = [{edition: 'mobileapp', version: '1.1'}, {edition: 'webapp', version: '3.0'}, {edition: 'companion', version: '1.0'}, {edition: 'eop', version: '5.0'}];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, as well, could be moved out and created up front as a configuration that the suiController just consumes.

}
</style>

/* EoP card styles derived from the original PDF design, See

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should preferably be externalised and generated as a stylesheet that the cardPreview.svelte uses.


{#if mappings}
<h2 id="mapping" class="title">{$t("cards.eopCardTaxonomy.h1.1")}</h2>
<MappingsList title="STRIDE:" mappings={formatValue(mappings['stride'])} />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This as well. The eopCardTaxonomy.svelte should be generalised so that it can read any type of taxonomy configuration JSON object and create the correct taxonomy based on how it looks. A separate taxonomy attribute in the YAML mapping could be used for this.

function cardFound()
function cardFound()
{
let cards_options = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good.

suits : SuitController.getSuits(),
decks : decks.set(lang, new Map([...mobileCards, ...webappCards, ...companionCards])),
suits: SuitController.getSuits(),
decks: decks.set(lang, new Map([...mobileCards, ...webappCards, ...companionCards, ...eopCards])),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ...mobileCards, ...webappCards, ...companionCards, ...eopCards this should simply be: ...decks.
For later. Not now.

import type { Suit } from "../../domain/suit/suit.js";
import { SvelteMap } from 'svelte/reactivity';
import { VERSION_WEBAPP, VERSION_MOBILEAPP, VERSION_COMPANION } from "$lib/services/deckServiceConsts";
import { VERSION_WEBAPP, VERSION_MOBILEAPP, VERSION_COMPANION, VERSION_EOP } from "$lib/services/deckServiceConsts";

@sydseter sydseter Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be moved out and made configurable later.

});
let eopSuits = $derived.by(() => {
const langSuits = suits?.get(`${VERSION_EOP}-${$lang}`);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

{#if map?.get(suit.name)}
{#each suit.cards as card (card)}
<p onmouseenter={()=>{enter(suit.name,cards?.get(card)?.id)}}>
<a title="OWASP Cornucopia {Text.Format(suit.name).toUpperCase()}, {cards?.get(card)?.id}" href={cards?.get(card)?.url ?? ''}>&#9500;&#9472;&#9472; {cards?.get(card)?.id}</a>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the OWASP Cornucopia prefix to the title might not make sense any more. How does this look like for the EoP cards?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could be taken from the incoming mapping or card data?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the tooltip shows "OWASP Cornucopia SPOOFING, SP2" for the SP2 card, so yes, the prefix is misleading here.
I can take it from the card data as you suggested: adding an editionName field to the card model, populated by deckService from a per-edition name map, and using it in the link titles. The title becomes "Elevation of Privilege SPOOFING, SP2" while the other decks keep their current text.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

<div class="card-buttons">
{#each suit.cards as card (card)}
<p>
<a title="OWASP Cornucopia card: {cards?.get(card)?.id} from suit: {Text.Format(suit.name).toUpperCase()}" href={cards?.get(card)?.url ?? ''}>&#9500;&#9472;&#9472; {cards?.get(card)?.id}</a>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well. The link title needs to come from the card or mapping data.

function cardFound()
function cardFound()
{
let cards_options = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

return isKnownCardId(card?.id)
}

function getEdition(str: string) : string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for now, but this should also come from the card and mapping data.

function cardFound()
function cardFound()
{
let cards_options = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be added here by the card browser script.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. Later, this should be copied here by the card browser script. All assets could come from a specific folder containing the yaml, images, css and taxonomy needed to create the card browser scaffolding.

assert Show.latest_version("webapp") == "3.0"
assert Show.latest_version("ecommerce") == "1.22"
assert Show.latest_version("eop") == "5.1"
assert Show.latest_version("eop") == "5.2"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this test makes sense. I think we should remove the latest_version assertions.

assert Show.latest_version("cumulus") == "1.1"
assert Show.latest_version("masvs") == "1.1"
assert Show.latest_version("eop") == "5.1"
assert Show.latest_version("eop") == "5.2"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all versioning assertions.

assert Show.latest_version("cumulus") == "1.1"
assert Show.latest_version("masvs") == "1.1"
assert Show.latest_version("eop") == "5.1"
assert Show.latest_version("eop") == "5.2"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the versioning assertions. It will make it harder to add new editions and doesn't really do anything important.

@sydseter
sydseter self-requested a review July 18, 2026 11:47
@sydseter
sydseter dismissed their stale review July 18, 2026 11:48

I won't require you to make any of these changes. It's up to Grant to prioritize.

@sydseter

Copy link
Copy Markdown
Collaborator

I would look at the title fields for the links before merging. The OWASP Cornucopia prefix will be misleading for the EoP cards in the card browser.

"cumulus" -> "1.1"
"masvs" -> "1.1"
"eop" -> "5.1"
"eop" -> "5.2"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should come from a configuration file that is generated up-front

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be copied here by a script

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration script could probably be generated as well from config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants