{"id":14956065,"url":"https://github.com/ipfs/ecosystem-directory","last_synced_at":"2025-10-01T17:30:40.939Z","repository":{"id":40328710,"uuid":"346190146","full_name":"ipfs/ecosystem-directory","owner":"ipfs","description":"Interactive showcase of projects and products built using IPFS, the InterPlanetary File System.","archived":false,"fork":false,"pushed_at":"2024-06-03T17:59:49.000Z","size":11476,"stargazers_count":15,"open_issues_count":3,"forks_count":8,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-01-14T11:55:53.464Z","etag":null,"topics":["decentralized","dweb","ipfs","web3"],"latest_commit_sha":null,"homepage":"https://ecosystem.ipfs.tech","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ipfs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2021-03-10T01:03:00.000Z","updated_at":"2024-06-03T17:59:53.000Z","dependencies_parsed_at":"2023-11-07T17:26:32.439Z","dependency_job_id":"1507f3df-01cc-463a-aff2-e151d2e8bea1","html_url":"https://github.com/ipfs/ecosystem-directory","commit_stats":{"total_commits":625,"total_committers":17,"mean_commits":36.76470588235294,"dds":0.6448,"last_synced_commit":"26fc39d3a1b6ec708964fda138f8c14006661e0c"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipfs%2Fecosystem-directory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipfs%2Fecosystem-directory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipfs%2Fecosystem-directory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipfs%2Fecosystem-directory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipfs","download_url":"https://codeload.github.com/ipfs/ecosystem-directory/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234883316,"owners_count":18901365,"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","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":["decentralized","dweb","ipfs","web3"],"created_at":"2024-09-24T13:12:15.698Z","updated_at":"2025-10-01T17:30:39.888Z","avatar_url":"https://github.com/ipfs.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IPFS Ecosystem Directory\n\nInteractive IPFS ecosystem directory and showcase\n\n**Just want to add a new project to the IPFS ecosystem directory? [Use this form](https://ipfs.fyi/ecosystem-form).**\n\n***\n\n## Project Add/Change Workflow\n\nThe existing workflow for adding a project to the ecosystem directory, or amending an existing project, is as described below.\n\n### Adding a New Project\n- Project info is added to the [master IPFS project database](https://airtable.com/tblxBjPTzHXiUVZAA/viwpijXTIFraPRkhE?blocks=hide) in one of two ways:\n     - Directly in the base as a new row\n     - By requesting a project's representative fill in the [IPFS Ecosystem Directory Submission Form](https://airtable.com/shrjwvk9pAeAk0Ci7)\n- Once a record is added to the database, an IPFS core team member reviews the record for accuracy, consistency, typos, etc, as well as determining whether the project should be included in the directory (a decision largely based on project maturity)\n- If the project is approved to be included in the directory (the \"Include in directory\" checkbox is ticked), a PR will be created automatically with the JSON by the [script](./scripts/fetch-from-airtable.js) which is run by [GitHub Actions](https://github.com/ipfs/ecosystem-directory/actions/workflows/airtable-pull.yml).\n\n### Amending an Existing Project\n\n- Project info should be amended in the [IPFS Builder Database (Ecosystem page)](https://airtable.com/tblxBjPTzHXiUVZAA/viwpijXTIFraPRkhE?blocks=hide) as a single source of truth\n- From there, update the project details and a PR will be created automatically by GitHub Actions (within 24 hours). You can also trigger the [GitHub Action](https://github.com/ipfs/ecosystem-directory/actions/workflows/airtable-pull.yml) manually by clicking `Run workflow`.\n\n***\n\n## Generating the Showcase Grid\n\nTo view all projects in the ecosystem directory in a \"logo parade\" showcase format suitable for including in a slide deck, [see the instructions below](#Showcase) — this includes how to customize the grid to get the representation that's most useful for you.\n\nTLDR: https://ecosystem.ipfs.tech/showcase/?category=focus is a general-purpose, useful grid if you don't want to build a custom one.\n\n## Behavior\n\n- The showcase view is visible in its bare form at [ecosystem.ipfs.tech/showcase](https://ecosystem.ipfs.tech/showcase)\n- It provides a logo-based visual summary of the projects in the app by category\n- Each tag within the selected category is assigned a block\n- The container for the tag is sized based on whether there's a small, medium, or large quantity of projects within it\n\n## Customization\n\n### Category\n\nCustomizing the view is done using GET parameters. You might notice that the base showcase link feels a little empty.\n\nAdding a target top-level `category` param to showcase solves this issue. Examples:\n- [/showcase/?category=industry](https://ecosystem.ipfs.tech/showcase/?category=industry)\n- [/showcase/?category=focus](https://ecosystem.ipfs.tech/showcase/?category=focus)\n- [/showcase/?category=benefits](https://ecosystem.ipfs.tech/showcase/?category=benefits)\n\n\u003e Note: This category selection will work for _both_ categories that allow one tag per project (category in which tags are mutually exclusive) and categories that allow multiple tags.\n\n### Containers\n\nInstances of this app may have varying quantities of projects. To help the showcase address both large and small ecosystems, there are params that define container limits. These define what the limits are in terms of number of projects that fit into medium, or large containers.\n\n- `md` minimum projects with a tag to display in a medium container (default is 10, if no value is provided)\n- `lg` minimum projects with a tag to display in a large container ((default is 25, if no value is provided)\n\nExample use case: [/showcase/?category=focus\u0026md=5\u0026lg=15](https://ecosystem.ipfs.tech/showcase/?category=focus\u0026md=5\u0026lg=15)\n\n\u003e Note: The Showcase view is designed to support rows of 5 logos, so setting `lg` and `md` as multiples of 5 is recommended for the ideal visual appearance.\n\n### Location\n\n- The showcase path may be redefined in the app's `settings.json` under the key `showcaseBaseRoute`\n  - Currently implemented as: `\"showcaseBaseRoute\": \"/showcase\"`\n- The showcase has no links, and is not discoverable by search engines\n\n***\n\n## General Developer Information\n\n### Deployment\n\nThis repo is currently deployed to Fleek on the following URLs:\n\n`main` branch: https://ecosystem.ipfs.tech/\n\n`develop` branch: https://ipfs-ecosystem-develop.on.fleek.co/\n\nPushes made to the `main` or `develop` branches of this repo will automatically be reflected in the URLs above. Please allow 2-5 minutes for the application to re-build before seeing changes in your browser.\n\n\n### Server\n\n**Ports**\n\nThese apply to local development.\n\n```\ndevelopment: 20000\nstable: 20001\nproduction: 20002\n```\n\n**Mode**: `static`\n\n**Environment Variables**\n\n```\nNODE_ENV → development|production\nSERVER_ENV → development|stable|production\n```\n\n***\n\n## Getting Started\n\n### A. Get Repo\n\nIn a terminal, run the commands below to get set up\n\n```bash\n# Navigate to directory within which to clone the git repo.\n$ cd ~/Desktop\n\n# Clone the repo\n$ git clone git@github.com:ipfs/ecosystem-directory.git\n\n# Enter the repo directory\n$ cd ecosystem-directory\n\n# Add your user information\n$ git config user.name \"Your Name\"\n$ git config user.email \"your.email@example.com\"\n\n# Install npm dependencies\n$ npm ci\n```\n\n### B. Generating a self-signed SSL certificate\n\nUsed for development in a local environment (such as on your personal computer). You only need to do this once. If you've already done this for a different project, just copy your existing `localhost_cert.pem` and `localhost_key.pem` files from `~/.ssh` into the root directory of this repo and skip the rest of this step.\n\n1. [Install mkcert and generate certificate](https://github.com/FiloSottile/mkcert) by running the commands below, in that order:\n\n  ```bash\n  $ cd ~/.ssh\n  $ brew install mkcert\n  $ mkcert -install\n  $ mkcert -key-file localhost_key.pem -cert-file localhost_cert.pem localhost 127.0.0.1\n  $ cat localhost_cert.pem \u003e localhost_fullchain.pem\n  $ cat \"$(mkcert -CAROOT)/rootCA.pem\" \u003e\u003e localhost_fullchain.pem\n  ```\n\n2. Copy the new `localhost_cert.pem` and `localhost_key.pem` files to the root directory of this repo\n3. Start the server and navigate to `https://localhost:\u003cyour_port\u003e`\n\n### C. Environment variables\n\nCreate a file called `.env` and put it into the root directory of this repo. Add the following environment variables:\n\n```\nNODE_ENV=development\nSERVER_ENV=development\n```\n\n### D. Start the app\n\n```bash\n$ npm run dev\n```\n\n***\n\n## Project Model\n\nBelow is an outline of the project model. A boilerplate JSON file can be found in `@/static/project-template.json`. This template file can be duplicated, filled out and renamed to create a new project. It is not necessary to generate a project ID since the filename of each project will serve as a project `slug`. The project logo must be placed in the `@/static/images/projects` directory and committed to git. Just the logo name (ex: `project-logo.svg`) needs to be added to the project JSON file, no need to write out the full path.\n\n### Naming Convention\n\nThis means that file names must be named after project names, and use lowercase and kebab-case. For example, an appropriate project `slug` would be `world-wide-web`, and therefore the corresponding file would be `world-wide-web.json`. It's important to not deviate between the file name and the `name` of the project.\n\n### Keys\n\nKeys should be retained when not in  use. This ensures that if anyone wants to add to the project, they immediately see all keys available in other projects, rather than searching for the model or accidentally using a data structure that doesn't match the model perfectly. This means empty and type checking is done by the app in a strict fashion.\n\n- `display`: toggle whether or not to display or hide the project from the results\n\n- `featured`: toggle whether this appears in the featured slider\n\n- `sortNumbers`: these labels and numbers will be used in the sort-by filter\n\n- `logo`: all logos will be in SVG format and must be placed in the `@/static/images` directory\n\n- `name`: name of the product\n\n- `org`: a product can belong to multiple organizations\n\n- `description`: 2 descriptions are needed. A long description that will be visible on the Project Single page and a short description that is visible in the card format (such as the Featured slider). If none provided, a truncated version of the long description will be used.\n\n- `primaryLink`: this is the 1st link found directly under the project description\n\n- `links`: these links will always appear at the top of the **Key Info** section, however, these links could be extended to use elsewhere in the future\n\n- `keyInfo`: these key/value pairs will always appear below the links in the **Key Info** section\n\n- `video`: can be a URL to either a YouTube or Vimeo video, the video must be publicly accessible and embeddable\n\n- `stats`:  general statistics with short descriptions, also known as \"big numbers\"\n\n- `ctaCard`: this card will always be displayed as the last block in the stats section. The button text can be changed in `@/content/pages/project.json`\n\n- `taxonomies`: the taxonomies contain a `slug` that will match a master taxonomy object (found in: `@/content/pages/taxonomy.json`); if a tag does not match, it will not be displayed. In other words, `taxonomy.json` acts as a source of truth, regardless of the tags assiged in a particular project's content.\n\n### Schema\n\nThe schema can be found [here](https://github.com/ipfs-shipyard/nuxt-module-ecosystem-directory/blob/main/content/data/project-schema.json) and _must_ be updated if the schema ever changes. The `$setProjectDefaults()` global method (found [here](https://github.com/ipfs-shipyard/nuxt-module-ecosystem-directory/blob/main/plugins/helpers.js)) uses [`project-schema.json`](https://github.com/ipfs-shipyard/nuxt-module-ecosystem-directory/blob/main/content/data/project-schema.json) in order to perform type checking.\n\n```ts\n{\n  display: Boolean,\n  featured: Boolean,\n  sortNumbers: {\n    label: Number\n  },\n  logo: {\n    icon: String,\n    full: String\n  },\n  name: String,\n  org: [String],\n  description: {\n    short: String,\n    long: String\n  },\n  primaryCta: {\n    url: String,\n    text: String\n  },\n  links: [ {\n    label: String,\n    links: [ {\n      url: String,\n      text: String\n    } ]\n  } ],\n  keyInfo: [ {\n    label: String,\n    value: String\n  } ],\n  video: String,\n  stats: [ {\n    label: String,\n    value: String\n  } ],\n  ctaCard: {\n    title: String,\n    description: String,\n    buttonText: String,\n    url: String\n  },\n  taxonomies: [{\n    slug: String,\n    tags: [String]\n  }]\n}\n```\n\n***\n\n## Transforming Project Data\n\nEach project that is to be included in the ecosystem must have a `json` file in `@/content/projects`, with the project name in `kebab-case`. For instance, `@/content/projects/foo-bar.json`.\n\n***\n\n## Embeddable View\n\nAn interactive summary of the state of the app can be injected into external sites. It contains a slider with sample projects that can be filtered by one dimension, and sorted. This view is embeddable in 2 steps:\n\n1. Include a single Javascript file in the external site, from the path `/embeddable-view.js`\n\n2. Specify a target container on the external site, with either a class or _id_ as follows\n  - `class=\"ipfs-ecosystem-embed\"`\n  - `id=\"ipfs-ecosystem-embed\"`\n\n\n### Customization\n\nThe content for the embeddable view, and target category for the dropdown, can be specified in the `embeddable-view-settings.json` file.\n\nThe embeddable view inherits the theming from the Ecosystem itself, but is also available in a light and a dark mode\n- For light mode (default), `data-theme=\"light\"` can be added to the target container\n- For dark mode, add `data-theme=\"dark\"` to the target container\n\nOn the external site, it might be useful to provide a `max-width` for the container\n```css\n.ipfs-ecosystem-embed, #ipfs-ecosystem-embed {\n  max-width: 600px;\n}\n```\n\nIf a class is used, multiple embeddable views can be instantiated on a single page. This is also useful for support of single page applications. The following HTML will call in two embeddable views, one light, and the other dark.\n\n```html\n  \u003cdiv class=\"ipfs-ecosystem-embed\" data-theme=\"light\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"ipfs-ecosystem-embed\" data-theme=\"dark\"\u003e\u003c/div\u003e\n```\n\n- The embeddable view is namespaced to avoid conflicts\n- This view calls in VueJS on the target site. If Vue is already loaded, it does not call it in\n- As such, this view is loosely compatible with legacy Vue 1 as well as Vue 3 sites (more extensive testing may need to be performed)\n- This view is compatible with all modern browsers, and many older browsers, including Internet Explorer 11\n\n***\n\n## Showcase\n\nThis application is shipped with an alternative data visualization, for the purpose of use in presentations and other media where a visual summary of the projects in the ecosystem is needed.\n\nBy default this view is called the _Showcase_, which is also the URL through which it's accessed. This path _can_ be changed.\n\n### Behavior\n\n- The showcase view is visible in its bare form at [ecosystem.ipfs.tech/showcase](https://ecosystem.ipfs.tech/showcase)\n- It provides a logo-based visual summary of the projects in the app by category\n- Each tag within the selected category is assigned a block\n- The container for the tag is sized based on whether there's a small, medium, or large quantity of projects within it\n\n### Customization\n\n#### Category\n\nCustomizing the view is done using GET parameters. The base showcase path will render projects, but may seem bare if no arguments are passed in the URI.\n\nAdding a target top-level `category` param to showcase solves this issue. Examples:\n- [/showcase/?category=industry](https://ecosystem.ipfs.tech/showcase/?category=industry)\n- [/showcase/?category=focus](https://ecosystem.ipfs.tech/showcase/?category=focus)\n- [/showcase/?category=benefits](https://ecosystem.ipfs.tech/showcase/?category=benefits)\n\n\u003e Note: This category selection will work for _both_ categories that allow one tag per project (category in which tags are mutually exclusive) and categories that allow multiple tags.\n\n#### Containers\n\nInstances of this app may have varying quantities of projects. To help the showcase address both large and small ecosystems, there are params that define container limits. These define what the limits are in terms of number of projects that fit into medium, or large containers.\n\n- `md` minimum projects with a tag to display in a medium container (default is 10, if no value is provided)\n- `lg` minimum projects with a tag to display in a large container ((default is 25, if no value is provided)\n\nExample use case: [/showcase/?category=focus\u0026md=5\u0026lg=15](https://ecosystem.ipfs.tech/showcase/?category=focus\u0026md=5\u0026lg=15)\n\n\u003e Note: The Showcase view is designed to support rows of 5 logos, so setting `lg` and `md` as multiples of 5 is recommended for the ideal visual appearance.\n\n#### Location\n\n- The showcase path may be redefined in the app's `settings.json` under the key `showcaseBaseRoute`\n  - Currently implemented as: `\"showcaseBaseRoute\": \"/showcase\"`\n- The showcase has no links, and is _not_ discoverable (`noindex, nofollow`) by search engines\n\n\n***\n\n## Analytics\n\nAnalytics are implemented through Countly, using the Web SDK.\n\nThe following environment variables are required:\n\n```bash\nNODE_ENV=\u003cproduction|development\u003e\nCOUNTLY_APP_KEY=\u003ckey\u003e\nCOUNTLY_SITE_URL=\u003curl\u003e\n```\n\nThe following `nuxt.config.js` entries are required:\n\n```js\n{\n  countly: {\n    initialize: true, // if set to false, Countly will be DISABLED\n    debug: Boolean,\n    disableInDevelopment: Boolean,\n    suppressErrorLogs: Boolean\n  }\n}\n```\n\nBelow is a breakdown of all events captured by Countly.\n\n### → General\n\n```js\nCountly.track_sessions()\nCountly.track_pageview()\nCountly.track_links()\n```\n\n### → Segment Chart\n\n**Segment clicked**\n\n`name: \u003ccategory_label\u003e`\n\n`slug: \u003ccategory_slug\u003e`\n\n```js\nCountly.trackEvent('Segment Chart | Segment Clicked', { label, slug })\n```\n\n**View All button clicked**\n\n`name: \u003ccategory_label\u003e`\n\n`slug: \u003ccategory_slug\u003e`\n\n```js\nCountly.trackEvent('Segment Chart | View All Button Clicked', { label, slug })\n```\n\n### → Featured Slider\n\n**Project card clicked**\n\n`name: \u003cproject_name\u003e`\n\n`slug: \u003cproject_slug\u003e`\n\n`from: Home Page | Detail Page`\n\n```js\nCountly.trackEvent('Featured Slider | Project Card Clicked', { name, slug, from })\n```\n\n### → Events\n\n**Filter Panel Toggled**\n\n`button: filters | x-icon | done`\n\n`state: open | closed`\n\n```js\nCountly.trackEvent('Filter Panel Toggled', { button, state })\n```\n\n**Sort-By Dropdown Toggled**\n\n`state: open | closed`\n\n```js\nCountly.trackEvent('Sort-By Dropdown Toggled', { state })\n```\n\n**Sort-By Option Selected**\n\n`label: \u003csort_option_label\u003e`\n\n`slug: \u003csort_option_slug\u003e`\n\n```js\nCountly.trackEvent('Sort-By Option Selected', { label, slug })\n```\n\n**Pagination Button Clicked**\n\n`page: \u003cnumber\u003e`\n\n```js\nCountly.trackEvent('Pagination Button Clicked', { page })\n```\n\n**Results-Per-Page Dropdown Toggled**\n\n`state: open | closed`\n\n```js\nCountly.trackEvent('Results-Per-Page Dropdown Toggled', { state })\n```\n\n**Results-Per-Page Option Selected**\n\n`option: \u003cnumber\u003e`\n\n```js\nCountly.trackEvent('Results-Per-Page Option Selected', { option })\n```\n\n**Grid-List View Toggled**\n\n`view: list | grid`\n\n```js\nCountly.trackEvent('Grid-List View Toggled', { view })\n```\n\n**Filter Chiclet Clicked**\n\n`tag: all | \u003ctag\u003e`\n\n`category: \u003cparent_category\u003e`\n\n`state: on | off`\n\n```js\nCountly.trackEvent('Filter Chiclet Clicked', { tag, category, state })\n```\n\n**Clear Filters Button Clicked**\n`count: \u003cnumber\u003e`\n\n```js\nCountly.trackEvent('Clear Filters Button Clicked', { count })\n```\n\n**Filter Panel Search Input**\n\nThere exists a 500ms debounce function in the search input so as to provide a greater chance of capturing a full rather than partial search query.\n\n`query: \u003csearch_term\u003e`\n\n```js\nCountly.trackEvent('Filter Panel Search Input', { query })\n```\n\n### → Header/Footer\n\nLinks get automatically tracked by `Countly.track_links()`\n\n### → 404\n\n```js\nCountly.trackEvent('404_NOT_FOUND', {\n  path: this.$route.path,\n  referrer: document.referrer\n})\n```\n\n### → Query params\n\nTrack URL entire query param object when interacting with project filtering system. Since every param change is already tracked individually as per the list above, this tracker exists as an added layer for testing.\n\n`query: Object`\n\n```js\nCountly.trackEvent('Query Param Debug', { query })\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipfs%2Fecosystem-directory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipfs%2Fecosystem-directory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipfs%2Fecosystem-directory/lists"}