{"id":13618326,"url":"https://github.com/Axinom/drm-quick-start","last_synced_at":"2025-04-14T10:31:28.177Z","repository":{"id":10635119,"uuid":"60837036","full_name":"Axinom/drm-quick-start","owner":"Axinom","description":"Quick start for Axinom DRM","archived":false,"fork":false,"pushed_at":"2024-09-24T10:55:37.000Z","size":1163,"stargazers_count":252,"open_issues_count":0,"forks_count":59,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-11T19:08:39.827Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/Axinom.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"License.txt","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":"2016-06-10T09:27:34.000Z","updated_at":"2025-04-07T00:42:57.000Z","dependencies_parsed_at":"2024-08-01T20:47:11.700Z","dependency_job_id":"160fab47-162f-45d9-b31c-78fcd444772d","html_url":"https://github.com/Axinom/drm-quick-start","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-quick-start","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-quick-start/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-quick-start/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-quick-start/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Axinom","download_url":"https://codeload.github.com/Axinom/drm-quick-start/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248862645,"owners_count":21173845,"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":[],"created_at":"2024-08-01T20:01:58.807Z","updated_at":"2025-04-14T10:31:27.525Z","avatar_url":"https://github.com/Axinom.png","language":"JavaScript","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"# Axinom DRM - quick start\n\nThis guide will show you how you can start using Axinom DRM to protect and \nplay back premium video content. \n\nThe DRM technologies offered by Axinom are: \n* Google Widevine \n* Microsoft PlayReady \n* Apple FairPlay Streaming\n\nThere are separate repositories that contain a simple implementation of an\n[Android frontend](https://github.com/Axinom/drm-sample-player-android) and\n[iOS frontend](https://github.com/Axinom/drm-quick-start-ios) that \nalso showcase the usage of Axinom DRM.\n\n# Axinom DRM overview\n\nThis chapter presents the basic concepts of DRM and provides a high-level \noverview of a common Axinom DRM usage workflow that is further demonstrated in \nthe following chapters. For more in-depth information about the involved \nprocesses and guidance on choosing a workflow best suited for your business \nneeds \n[Contact Axinom](mailto:info@axinom.com).\n\n## Content protection\n\nThe first phase in the use of DRM is the preparation of DRM-protected content. \nThis is illustrated by the following diagram:\n\n![](Images/Concepts-ContentProtection.png)\n\n1. Clear (unencrypted) content is provided to a DRM-capable encoder / packager.\n1. The encoder / packager contacts Axinom DRM Key Service and requests one or \nmore content keys (content encryption keys). \n1. The key service generates new keys and returns them securely to the \nencoder / packager, together with the key IDs.\n1. The encoder / packager uses the key information to encrypt the content and \npackage it in a format suitable for DRM-enabled adaptive streaming, and uploads \nthe output to an origin server.\n\n## Protected content playback\n\nThe second phase is the playback of DRM-protected content. To play a video, a \nDRM-capable player needs to decrypt the media. For this, it needs access to \nthe content keys, which are delivered in a **license** that also defines the \nconditions under which the content keys may be used (e.g. expiration). Licenses \nare generated by **Axinom DRM License Service** when a player sends the \nlicense service a **license request** together with a **license token**. \nThe license token proves that the player has the right to get a license and \ninstructs the license service how to configure and generate the license.\n\nThis process is illustrated by the following diagram:\n\n![](Images/Concepts-LicenseAcquisition.png)\n\n1. An end-user chooses a video in a media player's playlist and initiates \nplayback.\n1. The player requests the content from a content delivery network (CDN).\n1. Encrypted content is streamed from the CDN to the player.\n1. The player notices the stream is protected by DRM and prepares for license \nacquisiton by obtaining a license request (from a Content Decryption Module, \nor CDM, integrated into the playback platform) based on the media metadata and \nrequests a license token from the Entitlement Service.\n1. The Entitlement Service authenticates the end-user and generates a license \ntoken configured according to the content manager's business rules. \n1. The player then sends the license request, together with the license token, \nto the license service.\n1. License service authenticates the license token and generates a license \n(according to the information in the license token), which is then returned \nto the player.\n1. Playback starts (provided that the content keys are valid and playback \nrules are satisfied).\n\n## Solution components\n\nThe core Axinom DRM products are Axinom DRM Key Service and Axinom DRM License \nService. Both are offered as cloud services while Axinom DRM License Service \nis also available as an on-premises installation.\n\nThe remaining parts of a DRM-enabled solution must be composed of additional \nAxinom or 3rd party products, or custom-developed components.\n\nIn terms of freely available 3rd party playback applications, we recommend the\nfollowing robust and reliable players:\n* [Shaka Player](https://github.com/google/shaka-player)\n* [dash.js](https://github.com/Dash-Industry-Forum/dash.js)\n* [ExoPlayer](https://github.com/google/exoplayer) (Android) \n\nThe player ecosystem on other platforms is less straightforward - \n[Contact Axinom](mailto:info@axinom.com) for assistance in player selection \nand integration.\n\n# Sample scenario 1: ready to go demo video\n\nIn the first scenario everything has been prepared for you - there exists a \nsmall website with an integrated player and a couple of DRM-protected demo \nvideos that you can watch. All necessary DRM information is hardcoded into \nthe application.\n\nThe sample application can be used with the latest versions of the following \nbrowsers (the DRMs supported are in parentheses): \n* Google Chrome (Widevine)\n* Mozilla Firefox (Widevine) \n* Microsoft Edge (PlayReady and Widevine)\n* Microsoft Internet Explorer (PlayReady)\n* Apple Safari (FairPlay)\n\nPlease follow the instructions below to run this sample project and go through \nthe first sample scenario.\n\n1. Install [node.js](https://nodejs.org) \n1. Clone or download this Git repository (the one that you are currently \nreading).\n1. Open a command prompt window and go to the directory where you placed the \nrepository's files (e.g. *C:\\Source\\drm-quick-start*).\n1. Install required 3rd party packages by executing the following command: \n    ```\n    npm install\n    ```\n1. Run the application by executing the following command: \n    ```\n    node Server.js\n    ```\n    \n    ![](Images/Sample-StartApp.png)\n\n1. If everything went well, the output from this command will instruct you to \nopen [http://localhost:8120](http://localhost:8120) in your browser. Do so.\n1. Open the website and click one of the links: the selected protected demo video will play.\n\n![](Images/Sample-WebsitePlayer.png)\n\nIf you encounter any difficulties in getting the demo videos to play, inspect \nthe log messages shown in the browser's JavaScript console and in the command \nprompt window.\n\n![](Images/Sample-JsConsole.png)\n\n## Understanding sample scenario 1\n\nThe sample project implements a basic website that enables the user to select \na video and play it in a modern DRM-capable browser. By default, there are \nonly a few demo videos in the list but the later chapters will show you how to \nadd more.\n\nThe main building blocks of the sample are:\n\n* *Server.js* - creates an HTTP server that publishes the website, the catalog \nAPI and the Entitlement Service.\n* *VideoDatabase.js* - defines the list of videos made available to the user.\n* *CatalogApi.js* - implements the catalog API that is used by browser-side \nJavaScript code to obtain the list of videos.\n* *EntitlementService.js* - implements the Entitlement Service - an API that \nauthorizes playback requests coming from the browser-side JavaScript code and \nreturns license tokens. \n* *Website/index.html* - the page loaded in the browser, including the \nbrowser-side JavaScript code; it communicates with the catalog API and the \ntoken service using REST web service calls; the website integrates Shaka \nPlayer for video playback.\n\n![](Images/Sample-Workflow.png)\n\nIn terms of executed workflows, the following takes place:\n\n1. When the website is loaded in a browser, the browser-side JavasScript code \nfirst determines whether the browser supports FairPlay. \n1. If FairPlay is supported, then the Axinom FairPlay Test Certificate is \nloaded from an Axinom server and the integrated player is configured for \nFairPlay use.\n1. Catalog API is contacted to retrieve the list of videos. \n1. Catalog API returns the videos listed in *VideoDatabase.js*. The demo \nvideos are filtered by tags to only show those playable on the current \nbrowser.\n1. When the user clicks on a video link to start playback, the following will \ntake place.\n1. The browser-side JavaScript code requests a license token from the \nEntitlement Service. In production, this service should also authorize the \nuser.\n1. The Entitlement Service grants a token for every request, as there is no\nneed to actually refuse playback in the sample scenarios. If the website\nrequested permission to play one of the pre-defined videos then the\nEntitlement Service simply returns a hardcoded license token (this is a special\ncase to keep the first sample scenario simple); other scenarios introduced\nbelow will use a more realistic workflow, where a new token is generated upon\neach request.\n1. Upon receiving the license token, the browser-side JavaScript code \nactivates the embedded Shaka Player and instruct it to play the video, \nproviding both the video URL and the DRM configuration. For the rest of the \nprocess, the player code will be in control.\n1. The player detects that the video is protected and requests a license from \nthe license service, while also attaching the license token to the request.\n1. License service authorizes the license token and returns a license based on \nthe information in the token.\n1. Playback starts (provided that the content keys in the license are correct \nand playback rules are met).\n\nThe code is thoroughly commented, so the above is only a high-level overview. \nTo understand the details, please explore the source code.\n\n# Sample scenario 2: creating your own license tokens\n\nIn this scenario, sample project is going to be modified to generate a unique \nlicense token upon every request, instead of returning a hardcoded one.\n\n[Axinom DRM evaluation account](https://portal.axinom.com/mosaic/free-trial/) is\nneeded in order to proceed. Upon signing up, you will need to go to the \"DRM\" area \nfrom [My Mosaic](https://portal.axinom.com/mosaic/my-mosaic/) and you can request for a DRM \ntenant. With that you will receive a document containing DRM configuration \ninformation required below.\n\nTo modify the project for the second sample scenario: \n1. Open *VideoDatabase.js* and pick one of the pre-made video entries, which \nis going to be modified and worked with for the rest of this scenario. \n\n1. Depending on your browser, choose one of the following: \n    * On Safari: \"Axinom demo video - single key (HLS; cbcs)\"\n    * Other browsers: \"Axinom demo video - single key (DASH; cenc)\"\n\n1. If you are using [Axinom Encoding](https://docs.axinom.com/services/encoding),\nremove the hardcoded license token from the video and replace it with the \nfollowing `keys` list:\n    ```\n        \"keys\": [\n            {\n                \"keyId\": \"211ac1dc-c8a2-4575-baf7-fa4ba56c38ac\"\n            }\n        ]\n    ```\nor if you are using a sample video provided by Axinom, the key seed of the \ntenant that was used to encode the video will be different and you'll need \nto explicitly generate and provide a token in the video entry( VideoDatabase.js )\nthat contains the embedded encryption key for the media. We recommend to use the \nhttps://portal.axinom.com/mosaic/tools/entitlement-message tool for token generation.\n```\n        {\n            \"name\": \"Axinom demo video - single key (DASH; cenc)\",\n            \"url\": \"https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest.mpd\",\n             \"licenseToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjogMSwiY29tX2tleV9pZCI6ICI0N2RhM2NlMC04ZjFlLTQ4NDYtYTUwZi1hZTc0MDAzY2Y0MmMiLCJtZXNzYWdlIjogeyAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsICAidmVyc2lvbiI6IDIsICAiY29udGVudF9rZXlzX3NvdXJjZSI6IHsgICAgImlubGluZSI6IFsgICAgICB7ICAgICAgICAiaWQiOiAiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwgICAgICAgICJlbmNyeXB0ZWRfa2V5IjogIjgwOWxkUzVYM1VqU29ON1ovMjN6aFE9PSIgICAgICB9ICAgIF0gIH19fQ.OaOk2jS3KreIB4WCqBD4_0GI4S5Hb_yiWEORLRL-qCA\",\n        }\n```\n\n1. Create a *Secrets.json* file based on the sample below and place it in \nthe same directory as *Server.js*. **Replace the communication key below with \nreal values from the Axinom DRM Fact Sheet**. \n    ```\n    {\n        \"communicationKeyId\": \"00000000-0000-0000-0000-000000000000\",\n        \"communicationKey\": \"00000000000000000000000000000000000000000w==\"\n    }\n    ```\n\n    The **communication key** is used to digitally sign license tokens and \n    secure the transfer of sensitive data. The signature will be checked by \n    the license service to authenticate the token and to verify its integrity, \n    making the token impossible to forge. See the comments in \n    *EntitlementService.js* for more details.\n\n    Having created the *Secrets.json* file, you should see a message about it \n    being loaded when you start the application.\n\n    ![](Images/Sample-StartAppWithSecrets.png)\n\n    From now on the token service will generate a unique license token upon \n    every request for videos not using a hardcoded token. \n\n1. Run the application, open the website in a browser and go play the video!\n\nIf you encounter any difficulties in getting the demo video to play, inspect \nthe log messages shown in the browser's JavaScript console and in the command \nprompt window.\n\n## Understanding sample scenario 2\n\nThe logic for generating license tokens is already provided in \n*EntitlementService.js* and this functionality is activated by the \ninstructions above. In order to generate a license token, the token service \nneeds to know the IDs of all content keys that are to be made available to \nthe user. \n\n*Note: this pattern of key management is simplified compared to actual \nproduction use. See the chapter on security below.* \n\n# Sample scenario 3: creating your own videos\n\nIn this scenario the sample project is going to be modified to play back \nvideos that you create. \n\nTo proceed, please ensure that the following prerequisites are fulfilled:\n* The sample project modifications described in scenario 2 are already \nperformed.\n\n* Makesure you have access to the Mosaic Management system. You can acquire \na Management system for yourself via https://portal.axinom.com/mosaic/my-mosaic\n\n* Ensure that you have an MP4 or MOV file containing both video and audio. \nYou can download some free test content in this format from the \n[Blender Foundation](https://mango.blender.org/download/). During initial \nexperimentation it is recommended to start out with short clips to reduce \nmedia processing time.\n\nThe steps below will transform this your video file into a format suitable \nfor playback:\n\n1. Create Storage for an Input Storage for Axinom Encoding \n(this is where the source material will be uploaded to) and an Output Storage \n(this is where Axinom Encoding) will store the encoding results.\n2. Set up an Acquisition and a Publishing Profile.\n3. Set up a Processing Profile.\n4. Upload a video to the Input Storage (use a subfolder for each video).\n5. Start encoding. Wait until encoding is finished.\n\n### Create storage for Input\n\nThe simplest way to create a Storage is using a Mosaic Hosting Service.\n\nFollow instructions under \n[Storage with Mosaic Hosting Service](https://docs.axinom.com/platform/hosting/storage-with-mosaic-hosting-service/)\n\nWith a few clicks you will get a [Storage Account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview) in Microsoft Azure, which you can use from Axinom Mosaic, but also directly using tools such as[Azure Storage Explorer](https://azure.microsoft.com/en-us/features/storage-explorer/).\n    \n### Set up an Acquisition and a Publishing Profile\n\nLog in to your Mosaic Management System and follow instructions under \n[Set up Encoding Profiles](https://docs.axinom.com/services/video/setup-encoding-profiles/). \nAs a storage provider choose \"Microsoft Azure\". All the required configuration (such as, account name, account key, SAS token) you can copy from the previous step (Storage properties).\n\nYour secrets, such as your account key, shall be entered encrypted (with a certificate of Axinom Encoding), so that only Axinom Encoding can access your storage. To encrypt credentials, use [Credentials Protection Tool](https://portal.axinom.com/mosaic/tools/encoding-credentials-protection).\n\n### Set up a Processing Profile\n\nLog in to your Mosaic Management System. You will find Processing Profiles under Settings / Video Encoding.\n\nYou can create a new profile or edit the DEFAULT profile. The properties here impact the encoding process. check [Processing Profile](https://docs.axinom.com/services/video/setup-encoding-profiles/#processing-profile) document to have more understanding about the each property.\n\nWe recommend for the start leaving all values at their defaults. You can experiment with them later.\n\n### Upload the video\n\nUpload the video to your Input Storage. Select the container video-input, create a subfolder for your video and upload the .mp4 file there. Just a single .mp4 file is enough.\n\nTo upload a file to your storage you can use a tool such as Azure Storage Explorer.\n\nAlternatively, you can upload files directly in Axinom Portal. Go to My Mosaic / Encoding and select \"Upload a Source Video\". This module uploads files to your storage previously created with the Mosaic Hosting Service.\n\n![](Images/Portal-upload-video.png)\n\n### Encode the video\n\n1. To start encoding follow the steps below:\n2. Log in to your Mosaic Management System.\n3. Go to \"Videos\", then \"New\". You will get a list of folder in your configured Input Storage.\n4. Select the folder with your video\n5. Click Start encoding.\n\nEncoding process takes some time (the longer the video, the longer the process). You can observe the progress looking at the logs, accessible from the respective station.\n\nOnce encoding is finished, you can copy the link to the DASH manifest and play it with any DASH-capable video player.\n\n### Add the video details to *VideoDatabase.js*\n\nHaving created the video, add a matching entry to VideoDatabase.js. You need to provide a video name, the URL to one of the manifest files and the Key ID used in encrypting the video or a token with key and the keyID.\n\n    ```\n    {\n        \"name\": \"My video 1\",\n        \"url\": \"http://localhost:8120/Video1/Encrypted_Cenc/Manifest.mpd\",\n        \"keys\": [\n            {\n                \"keyId\": \"60447277-19b2-4367-a1e0-da543aee2da0\"\n            } \n        ]\n    }\n    ```\nNote: when evaluating FairPlay, e.g. on Safari, use the URL of the HLS-CBCS manifest (Encrypted_Cbcs/Manifest.m3u8); otherwise use the DASH-CENC manifest (Encrypted_Cenc/Manifest.mpd).\n\nThat's it! You can now start the application, open the website and play \nyour video!\n\nIf you encounter any difficulties in getting the demo video to play, inspect \nthe log messages shown in the browser's JavaScript console and in the command \nprompt window.\n\nFollow the same process to play videos created with 3rd party tools. As each \nmedia processing product operates differently, universal instructions cannot \nbe provided here. [Contact Axinom](mailto:info@axinom.com) for detailed \nsupport in setting up your media workflows.\n\n# Sample scenario 4: creating your own multi-key videos\n\nWhen working with high-value content, content owners may require the usage of \ndifferent keys for different tracks and quality levels. \n\nCurrent scenario, very similar to the previous 3rd scenario, demonstrates how \nto prepare multi-key content. It will use UI for all operations \n(Mosaic Management System) to create multi-key \ncontent similar to the predefined \"multikey\" demo videos in \n*VideoDatabase.js*, where the FHD and HD tracks (1080p and 720p), SD tracks \n(480p, 360p and 288p) and the audio track are encrypted with different keys. \n\nIn order to proceed, please ensure that the following prerequisites are \nfulfilled:\n* Scenario 3 is completed and understood.\n* Make sure the input video has at least 720p resolution. Otherwise HD-track \nspecific keys cannot be applied, and your content will end up being encrypted \nwith fewer different keys.\n\nSteps for multi-key content preparation:\n\n1. While Setting up the Processing Profile as mentioned in the scenario 3, you\nwill have to select the *DRM Protection* to Multi Keys.\n![](Images/DRM-protection-selection.png)\n\n1. Then run the encoding process.\n\n1. Wait for the video to be processed. When finished, the output location will \nhave similar contents as in scenario 3. However, if you open the HLS or DASH \nmanifests of the encrypted content with a text editor, you'll notice that now \ndifferent tracks are associated with different content key IDs.\n\n1. Add a matching entry to *VideoDatabase.js*, similarly to scenario 3. \nThe only difference is that this time multiple Key IDs need to be \nspecified - all three that were used in the encryption of this video. \n\n    ```\n    {\n        \"name\": \"My video 2\",\n        \"url\": \"http://localhost:8120/Video2/Encrypted_Cenc/Manifest.mpd\",\n        \"keys\": [\n            {\n                \"keyId\": \"f3d588c7-c17a-4033-9035-8db317390be6\"\n            },\n            {\n                \"keyId\": \"44b18a32-6d36-499d-8b93-a20f948ac5f2\"\n            },\n            {\n                \"keyId\": \"ae6e87e2-3c3c-46d1-8e9d-ef4c461d4681\"\n            }, \n        ]\n    }\n    ```\n\n    Note: when evaluating FairPlay, e.g. on Safari, use the URL of the \n    HLS-CBCS manifest (*Encrypted_Cbcs/Manifest.m3u8*); otherwise use the \n    DASH-CENC manifest (*Encrypted_Cenc/Manifest.mpd*).\n\n1. That's it! Start the application, open the website and play your \nmulti-key video.\n\n# Moving onward to real-world usage\n\nIn a real-world scenario there are potentially many more aspects to consider. \n\n* Axinom DRM License Service and Key Service are highly flexible and can \naccommodate to a wide range of scenarios:\n\n    * Axinom DRM can work in different modes - it supports working with key \n    seeds as well as individual keys.\n\n    * Axinom DRM Key Service supports several key delivery protocols out of \n    the box, including AWS SPEKE and Google Widevine Common Encryption API\n\n    * Axinom has complementary offerings that can help create a comprehensive \n    video streaming solution: Axinom VIP - for encoding / packaging, Axinom \n    Player SDK - for secure playback, and Axinom CMS - for managing content.\n\n* To keep the sample code straightforward, simplifications have been made. \nThe following additional aspects should be considered when planning a \nreal-world deployment:\n\n    * The Entitlement Service shown here authorizes every user for every \n    playback request and produces tokens with very relaxed playback \n    restrictions. Adjust this behaviour according to your business needs, so \n    that only the intended users can play back content under intended terms.\n\n    * Your website or client app should use HTTPS in order to protect against \n    the interception of communications (e.g. to steal the license token \n    generated by the token service). Also, if your website/player is not \n    served on localhost, it must be accessed using HTTPS to enable DRM \n    playback.\n\n    * You must carefully plan the life cycle of license tokens to match your \n    business scenario. While the sample project requests a license token \n    immediately before playback, this need not always be so. Some scenarios \n    may benefit from long-lived license tokens generated well in advance of \n    playback, thereby reducing network traffic. On the other hand, long-lived \n    license tokens enable a greater degree of misuse if they are ever stolen; \n    short 5-minute license tokens enable a stricter level of control for \n    scenarios where that is a concern.\n\n[Contact us](mailto:info@axinom.com) to learn more about our offerings and to \nget support in setting up your production environment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAxinom%2Fdrm-quick-start","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAxinom%2Fdrm-quick-start","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAxinom%2Fdrm-quick-start/lists"}