Nikolai Ryzhikov
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # SMART App Launch & Aidbox telegram - https://t.me/c/1525984429/25903 ## What is SMART? SMART on FHIR - is a standard way to reuse applications between FHIR systems and a way to extend FHIR systems. Essentially it is about how to delegate access to clinical data to third party applications on behalf of patient or provider. * Techinically it is OAuth spec extension, which introduces scopes, jwt claims, launch flows, app registration etc. * There are apps acting on behalf of the user (patient or provider) or backend services operating at the system level * There are 2 versions v1 and v2 (v2 is not yet released) ## SMART & Aidbox * SMART as a way to extend Aidbox. (i.e. unify with aidbox apps) * SMART as a way to extend system on top of Aidbox * SMART as key component for das Portal ## Customer Use Cases * Implement Portal with FHIR * For regulation like G10 or Member API * To extend system with third-party apps There are two important options: 1. Customer has its own identity management and we have to integrate 2. Customer does not have identity management ## Why? - FHIR server SHALL support Smart app launch - G10 - Health Plans patient access API regulations - PHR & EHR needs SMART support (future) - Patient portal as HS product (future) - Aidbox APP as Smart APP (Backend Services) ## Why now? - SMART App Launch is reauired by customers and leads - We have 2 separate implementations in Aidbox forks (SMARTbox and Healthplans) ## DOD - [ ] Review all related tasks - [ ] Inferno tests - [ ] Documentation how to connect smart app ## What we do not like about current solution? - Нет экспертизы - Заставить всех пройти Inferno тесты - Форк - не мейнтейнится - Начать мейнтейнить ## Research - All multitenancy combinations - FHIR Consent as Grant = (set of scopes with user_id/patient_id and client_id ) - Scopes and Security Labels as params on scope - Aidbox APP as Smart APP - How to link Patient with User? (Practioner) - Multiple linked Patients/Practitioners - Session per each linked resource - Patient.link? ### What is Patient API? Patient API refers to a group of FHIR server operations that allow access to a specific Patient resource and related resources. Implicitly, when the term `Patient API` is mentioned, it means there should be an additional access control layer that restricts access to resources not belonging to the specified patient. SMART on FHIR doesn't state anything about access control, and this is actually an end resource-server concern. Instead, SOF defines utilities to help an end-resource server perform access control. Scopes are actually one of the main 'utilities' provided by SOF. Scopes have different levels, which are: - Patient - User - System In the context of Patient API, we are only interested in `patient`-level scopes. Patient-level scopes signal that the client (a.k.a. Smart App) will act on behalf of a user associated with that Patient resource. For example, a client requested access with the following scope `patient/Patient.read` (or `patient/Patient.r` for SmartV1 scope style) and the authorization server issued an access token. All requests made by the client will be in the context of one specific patient. Next, the client issues the following request: ```rest GET [base]/Patient ``` What should happen next? Obviously, only the Patient resource on whose behalf the client acts should be returned, not every possible Patient resource on the server. Once again, the specification doesn't provide implementation guidance on how to perform such access control, nor should it. Similarly, OAuth 2.0 doesn't specify how resource servers should implement end access control. All implementations of such access control in Aidbox rely on the following steps: 1. Construct wrappers for generic FHIR Read interactions. These wrappers enforce (inject) specific search parameters to be present in the request, namely `_id` and `patient` parameters. These parameters are supplied with the user's patient ID, which is gathered from the session, ensuring that the resulting data set will contain only data instances related to the specific patient. 2. Hide these wrapped operations under a prefix at the routing level, for example, `[base]/patient`. 3. Add a frontend middleware (first in the middleware stack) or AccessPolicy that checks that the client has sufficient authority with their scopes to perform the interaction. Solution we see: #### Middleware to rule them all ##### Why not AccessPolicy? AccessPolicies are beneficial as they provide an opaque mechanism for an Aidbox platform end-user that can be easily tweaked and manipulated. This means we don't need to provide an additional pile of environment configurations in case of emergencies. However, Patient API access control can't be solved using AccessPolicies alone. ###### Why? With `GET [base]/Patient` fashioned requests, we can easily check that the client **has** the appropriate scope for performing such interactions, but AccessPolicies **can't do anything about the resulting data set**. For example, when a client performs a GET `[base]/Observation` request, we can use a pre-generated access policy to check that the client actually has the appropriate scopes to perform this interaction. However, what about the resulting set? It will obviously contain all possible observation resources present on the server. Someone might say, *"Oh, just enforce the search parameters to be present via AccessControl"*, for example: ```yaml resourceType: AccessPolicy id: test-policy engine: matcho matcho: params: patient: some-patient-id ``` This AccessPolicy requires the search parameter `patient` to be present and equal to a pre-defined id, basically you put part of the AccessControl on the clients's shoulders. But the immediate answer would be *"no"*. Because even simple smart apps such as **Billirubin** or **GrowthChart** send just generic requests such as: ```rest GET [base]/Observation?code=6505-5 ``` to an end-resource server. They don't specify specific search parameters with a specific patient ID, *nor do they need to*. ##### What's the solution? Add the middleware into the Aidbox web stack that: 1. checks whether the client referenced in the current session is a smart app. 2. It checks client scopes and decides if it's possible to perform the requested interaction. 3. If the interaction is possible, it injects search parameters such as `_id`/`Patient` to constrain the resulting data set. This raises the question, *"Can I add include/revinclude in my request and exploit it to access data that doesn't belong to me, for example:* ```rest GET [base]/Provenance?_id=prov1&_include=Provenance:based-on ``` *How can we limit that?"* 4. The answer is to perform post-filtration using CompartmentDefinitions. We can filter out resources from the resulting bundle that don't actually belong to the `Patient` using known scopes AND CompartmentDefinitions or FHIRSchema as a meta source for possible reference locations. If a resource doesn't pass this filtration check, we can either omit it or replace it with an OperationOutcome resource, indicating that some resource is not accessible. Middleware pseudo-code: ```: Request issued from client -> Naive access control: Is the client attempting to access an interaction a smart app? if yes: Gather scopes granted to this client. if the accessed interaction is allowed: Pass the request to the next step. else: Respond with 403 Forbidden. else: Skip this access engine. -> Inject search parameters: if the interaction is not with a Patient resource: Add `patient` search parameter to the initial request parameters. else if it is a Patient read interaction: Add `_id` search parameter to the initial request parameters. -> Check for join-semantics search parameters (include, revinclude, has, etc.): if such parameters are present in the initial request: Filter the resulting dataset: if the resource is accessible based on the current client scopes: Gather all possible patient reference locations in the resource using patient-CompartmentDefinition or FHIRSchema. Check these references on the resource instance. if all references point to the user patient: Keep the resource. else: Filter out the resource OR replace it with an OperationOutcome. else: Filter out the resource OR replace it with an OperationOutcome. else: Skip filtering. -> ``` - What to do with all legacy?? ## What to do ??? - [ ] Client UI impruvements (for SMART) - [ ] Patient API ?? - [ ] Promo - [ ] Smart app example on Aidbox SDK in https://github.com/Aidbox/app-examples - [ ] Patient / Provider - [ ] Review (write) docs How to connect existing SMART App into Aidbox ## Current state - No documentation on how to create a client for a FHIR Smart App / launch a Smart App - The old Grant UI is displayed - Access control does not consider the allowed scopes for the FHIR Smart App client - allowed to create any resources - allowed to retrieve arbitrary resources ## Competitors!!! ### Pairs - SMILE - @all - Mput & Rublag - Medplum Google - Panthevn & Evgen - Kodjin Firely - Rost & Ken - MS AWS - Mike & Marat - Keycloak OKTA Intersystem ### All cometitors * SMILE - https://smilecdr.com/docs/smart/smart_on_fhir_introduction.html * Medplum - https://www.medplum.com/docs/integration/smart-app-launch * Google - https://cloud.google.com/healthcare-api/docs/smart-on-fhir * SMART proxy - https://github.com/GoogleCloudPlatform/smart-on-fhir ### Firely Link: [Firely Server Security Documentation](https://docs.fire.ly/projects/Firely-Server/en/latest/security/smart.html#) #### Main Points: * Supports external identity providers. * As tokens use JWT, specific claims are expected to be present. * First-class citizen SMART scopes, AccessControl engine mainly relies on them * Patient-level scopes utilize CompartmentDefinitions and internal filters for access control. * User/system-level scopes are processed differently, not relying on CompartmentDefinitions, allowing access to any resource permitted by the scope. These levels must be managed via Plugins (code extensions of Firely server) or AccessPolicies. #### Proprietary Solutions for Authorization? Firely Auth acts as an OAuth 2.0 provider with extensions for SmartOnFhir, including scope/launch context awareness and smart-specific endpoints. Can integrate with external identity providers, docs shows [example](https://docs.fire.ly/projects/Firely-Server/en/latest/security/firely-auth/firely-auth-sso.html#using-microsoft-entra-id-formerly-azure-active-directory) with Entra Id #### External Auth Servers: The documentation states, with guides on using Active Directory (AD), that Firely Auth can be replaced with any OAuth 2.0 provider capable of handling SOF extensions and passing JWTs with the required claims. ![Firely Auth Setup](https://hackmd.io/_uploads/BygXBjrwR.png) #### Setup: To enable authorization, you need to install a specific [plugin](https://docs.fire.ly/projects/Firely-Server/en/latest/security/smart.html) into your Firely server. The configuration points to the OAuth 2.0 Provider base URL and requires the user to provide filters per CompartmentDefinition, which will be covered in detail in the next section. #### [Authorization Process](https://docs.fire.ly/projects/Firely-Server/en/latest/security/tokens_and_compartments.html) (Patient-Level Scopes Only): 1. Parse and validate the JWT. 2. Check that the requested interaction is covered by the scopes claim in the JWT. 3. CompartmentDefinition access control: * Check `launch` scopes. * For every mentioned launch scope, find the relevant claim in the token (e.g., `launch/patient` - `patient` claim). * For every found `launch` scope, find the relevant CompartmentDefinition based on the `CompartmentDefinition.code` value. * Based on the filter configuration provided during plugin setup and CompartmentDefinition parameters, inject filters into the initial request. ![Example of filter configuration](https://hackmd.io/_uploads/S1M-6irD0.png) ![Example of filter injection](https://hackmd.io/_uploads/HyMN6irvR.png) * For Create/Update operations, resources are checked against the filters mentioned earlier before insertion. If a resource is not present in the CompartmentDefinition but there is a scope for this resource, all resources of this type will be returned, including those not belonging to the Patient. Firely allows defining your own CompartmentDefinitions for any resource type, although this is prohibited by the FHIR Spec. #### User/System-Level Scopes: For user/system-level scopes, CompartmentDefinitions are not checked, only the scopes for required interactions. For additional access control, you need to define permissions (AccessPolicies). For more information on access control permissions, refer to the [Firely Server Permissions Documentation](https://docs.fire.ly/projects/Firely-Server/en/latest/security/permissions.html#feature-accesscontrol-permissions). * Microsoft - https://learn.microsoft.com/en-us/azure/healthcare-apis/fhir/smart-on-fhir * AWS HealthLake - https://docs.aws.amazon.com/healthlake/latest/devguide/smart-on-fhir.html * InterSystems - https://openexchange.intersystems.com/package/workshop-smart * Keycloak - https://github.com/Alvearie/keycloak-extensions-for-fhir * OKTA - https://github.com/oktadev/okta-smartfhir-demo MATRIX? ## Kodjin https://docs.kodjin.com/onc-smart-on-fhir-authorization/ Server natively supports the open-source OAuth 2.0 provider - Keycloak PKCE flow doesn't supported ### Flow - The application registers with the Kodjin Server - The application requests authorization - The Kodjin Authorization Server waits for the user's consent (scopes, login/pass) - The application exchanges the code for an Access Token - The application gains access to the user's data by presenting a Bearer token - Searching for Patient scopes (patient/Observation.read) is always implied by the search parameter patient ### Scopes Evaluation - Support Scope v1 - Expands wildcard * scope (patient/*.read -> patient/Patient.read ...) - If the user denies all requested scopes, the request will fail. - If the user grants one or more requested scopes, the request will succeed. ## Featrues - [ ] App Launch (Standalone & EHR) - [ ] App registration - [ ] App authentication (secret & jwt) - unify with aidbox client (app) - [ ] Concent & Scopes integration - [ ] Patient API (Compartment API?) ### Patient API Safe API to access patient data, no risk to expose more than one patient. It is very close to Compartment API ( `/Patient/[id]/Observation`) and probably can be configured with CompartmentDefinition ( the problem is with write access) Technically, patient api injects filters with patient id. For write it may validate or inject references. ## Read more * spec - https://build.fhir.org/ig/HL7/smart-app-launch/index.html * long talk about SMART by josh - https://youtu.be/lMJnw6YDz5E?si=f1Ec95UACJ54e6TD * Samurai Seminar: [g10 discussion](https://samurai.zendoc.me/seminars.g10-discussion) (high-level discussion of how smartbox works, not much technical details). ## zulip topics * Keycloak for SMART authz:168 * ✔ reasonable search assumptions on endpoints:109 * OAuth2 and Consent:102 * SMARTv2 Connectathon, Jan 2021:87 * Health Cards:79 * September 2020 Connectathon:77 * Web messaging, scratchpad:71 * system + patient scopes:62 * Granular scopes:57 * Backend Services - Public keys:55 * Argonaut Announcements:53 * SMART Patient Access Brands Organization Example & HTI-1:52 * Refresh tokens:48 * SMARTv2 Cerner Endpoint: aud error:47 * PKCE:46 * Connectathon 28:45 * C25 -- Testing with Cerner:44 * SMART scopes v2:41 * Launch Context:41 * SMART App Launch v2:39 * Dynamic URL Port:39 * C25 -- Testing with Epic:38 * How do apps convey error information:38 * Epic app registration:36 * Standalone Patient Initiated SMART:36 * ✔ Support for IDPs that do not support "/" in scopes:35 * Refresh token for online access:34 * reason for request:34 * patient/*.read vs Patient compartment:32 * can I get API from Epic in the same way I get from DrChrono?:32 * Authorization for transactions:32 * SMART Web Messaging - ballot comment reconciliation:31 * Deprecating authorize-post?:31 * Multiple Audiences:31 * Unable to get state from launch:31 * prefetch:30 * Granular Scope Approve / Deny Reporting:29 * Is `context-standalone-patient` required by the ONC rule?:29 * backend services oauth client audience input in the token...:29 * Client requesting combination of patient/* and user/* scopes:28 * online_access scope with SMART App Launch 2.0.0:28 * .well-known/smart-configuration registered with IETF?:27 * SMART on FHIR EHR Launch flow:27 * SMART for multiple patients:26 * Patient vs User Scope:26 * Token Validity:25 * security questions:24 * SMART on FHIR with Document Bundles:24 * codes and systems:24 * backend flow, ssi:24 * Launch Sequence:23 * Azure AD:23 * CapabilityStatement URIs:23 * C25 -- Testing with smart.argo.run:23 * SMART Apps: well_known or Capabilities?:22 * App launcher ISS JWT:21 * Context-dependent authorization / authz:21 * Volume of Smart Scopes:21 * Brands Connectathon:21 * US-Core profiles for validation:21 * online access not core?:20 * The custom claims of SMART App Launch:20 * Smart Framework Supported by Patient Apps:19 * Lauch within EPIC EHR:19 * 'Optional' launch parameters:19 * Capitalized headers in SMART:19 * revoke vs manage:18 * registering an app:18 * Access Token Payload: Endpoint Reference:18 * Newbie question re scopes:18 * "federating" FHIR for patient app:18 * launch context for standalone:18 * Constraining backend to a single patient?:17 * SMART web messaging: ballot URL:17 * App Launch Implementation:17 * Using SMART scopes for authorizing Operations:17 * Confidential client required for refresh tokens:17 * Web Messaging c10n:17 * SMARTv2 Epic Endpoint:16 * Resolving FHIR-32251:16 * ✔ SMARTv2 PKCE Requirements:16 * Epic App Orchard Hyperspace simulator:16 * fhirUser when >1 resource type applies:16 * Do you support PKCE in your OAuth2 server?:15 * Launch Context Type and Role:15 * pass patient:15 * SMART scopes for operations:15 * is `jwks_uri` support required for SMART server?:15 * Connectathon: SMART Launch JWKS:14 * Launching SMART app with parameters:14 * does the launch/encounter scope limit data access?:14 * SMART on FHIR app distributed architecture:14 * Patient group scopes:14 * HL7 Australia localization of SMART App Launch:14 * Where is the OperationDefinition for $HealthWallet.issueVC:14 * Double Login:14 * SMART app deployment for multiple clients:14 * client-js best practices:14 * Azure Server-ONC Certification (g)(10) -9.3 Token Revocation:14 * Smart and/or Security Guidance and/or IUA:14 * fhirContext and Inferno testing:14 * SMART App Launcher HTTP 401:14 * OAuth 2 with Smart on FHIR App:13 * should a launch fail if `launch/encounter` can't be met?:13 * Switching patient context for provider launch:13 * /metadata vs /.well-known:13 * Clarification of requiring nonce in id_token if provided:13 * Patient launch parameter vs fhirUser claim:13 * Token Introspection Endpoint:13 * Handling irrelevant scopes:13 * Registration with EHR:13 * FHIR OAuth Scopes Discussion:13 * SMART Launcher Request depercation of POST:12 * FHIR-32325:12 * launch.smarthealthit.org signing algorithms:12 * Consent to record user's authorization:12 * Patient Scope and User scopes:12 * Patient context with user-level scopes:12 * SMART Backend Services and SMART App Launch:12 * introspection:12 * Connectathon: Jan 2022:12 * Preferable SMART launch pathway:12 * adding parameters to the SMART launch string:12 * Standalone app and patient id:12 * ✔ SMART launch with user level scopes and launch/patient:11 * Server response when client sends bad launch parameter:11 * Question on FHIR API behavior with user level scopes:11 * Specs drift:11 * Security considerations for apps with a server component:11 * How can a SMART on FHIR App close itself?:11 * SMART on FHIR dotnet application:11 * ✔ Implementing `state` safely:11 * Patient launch context and multiple patient IDs:11 * Epic backend services:11 * Provider offline access:11 * context-ehr-encounter:11 * Jan Connectathon: Track Lead Volunteers?:11 * Asking patients for consent:11

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully