Skip to content

Commit c65b7e0

Browse files
committed
feat: /how-to/detect-ipfs-on-web
This adds a page with some best practices around detecting IPFS resources on the web, based on recent discussions with Brave team and our effort to streamline implmenetation there.
1 parent 1a1ec0d commit c65b7e0

File tree

2 files changed

+147
-1
lines changed

2 files changed

+147
-1
lines changed

docs/.vuepress/config.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ module.exports = {
234234
collapsable: false,
235235
children: [
236236
'how-to/address-ipfs-on-web',
237+
'how-to/detect-ipfs-on-web',
237238
'how-to/create-simple-chat-app',
238239
'/how-to/browser-tools-frameworks'
239240
]
@@ -443,7 +444,25 @@ module.exports = {
443444
],
444445
'vuepress-plugin-chunkload-redirect',
445446
'vuepress-plugin-ipfs',
446-
'vuepress-plugin-mermaidjs',
447+
[
448+
'vuepress-plugin-mermaidjs',
449+
{
450+
securityLevel: 'loose', // safe as we dont allow mermaid in user content. allows for additional interactivity
451+
theme: 'base',
452+
themeVariables: { // values from 'IPFS brand sheet' at https://github.com/ipfs/ipfs-gui#resources
453+
primaryColor: '#D7EDF1',
454+
edgeLabelBackground:'#edf0f4',
455+
tertiaryColor: '#edf0f4'
456+
},
457+
deterministicIds: true,
458+
deterministicIDSeed: DEPLOY_DOMAIN,
459+
flowchart: {
460+
htmlLabels: true,
461+
curve: 'basis',
462+
useMaxWidth: false
463+
}
464+
}
465+
],
447466
'tabs'
448467
],
449468
extraWatchFiles: ['.vuepress/nav/en.js']

docs/how-to/detect-ipfs-on-web.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Detect IPFS on the Web
2+
3+
Below is the suggested heuristic for detecting content-addressed resources on the web, and presenting additional user interface for interacting with them.
4+
5+
The goal of this page is to provide some suggestions and best practices for web browsers and other [user agents](https://en.wikipedia.org/wiki/User_agent) that are interested in adding support for [content-addressed resources on the Web](/how-to/address-ipfs-on-web).
6+
7+
## Detecting content-addressed resources
8+
9+
In many cases IPFS resource can be detected just by looking at URI, especially if it is a HTTP URL pointing at a public gateway. This allows browser to upgrade transport protocol to IPFS, and removed the need for sending HTTP request to a remote server.
10+
11+
The high level heuristic is to check, in order:
12+
- Does the `URL` follow [path or subdomain gateway](/how-to/address-ipfs-on-web/) convention?
13+
- If not, does [DNSLink](/concepts/dnslink/) with an IPFS path exist for `URL.hostname`?
14+
15+
If any of the above is true, an user agent should be able to find a content path that can be used for data retrieval over IPFS. See the illustration below.
16+
17+
```mermaid
18+
flowchart TD
19+
Start[Top-level navigation event]
20+
IsGateway{Is it a Gateway URL?<br/><code>foo.tld/ipxs/id/..</code><br/><code>id.ipxs.foo.com/..</code>}
21+
22+
Start -->|new URL in address bar| IsGateway
23+
IsGateway ==>|YES| IsPathOrSubdomain
24+
IsPathOrSubdomain -->|Path Gateway| ExtractFromPath
25+
IsPathOrSubdomain -->|Subdomain Gateway| ExtractFromSubdomain
26+
27+
subgraph GwURLToPath [Convert Gateway URL to a Content Path]
28+
IsPathOrSubdomain{Is the Gateway URL 'path' <br/> or 'subdomain' based?}
29+
ExtractFromPath[Extract <br/> <code>URL.pathname</code>]
30+
ExtractFromSubdomain[Read namespace <br/> and <br/>identifier from <br/> <code>URL.hostname</code> and <br/> prepend to <code>URL.pathname</code>. <br/> E.g., turn <br/><code>id.ipxs.foo.com/pathname</code> <br/> into <code>/ipxs/id/pathname</code>]
31+
end
32+
33+
PotentialContentPath[Found a Potential Content Path]
34+
35+
ExtractFromPath --> PotentialContentPath
36+
ExtractFromSubdomain --> PotentialContentPath
37+
PotentialContentPath -->|/ipxs/id/sub/path..| IsIpfsOrIpns
38+
39+
subgraph PathValidation [Is the Content Path valid?]
40+
IsIpfsOrIpns{Is it <code>/ipfs/</code> <br/>or <code>/ipns/</code><br/>?}
41+
ValidateRootCID{Is <code>id</code> <br/>a valid CID?}
42+
ValidateIPNSKey{Is <code>id</code> <br/>a valid CID <br/> with <code>libp2p-key</code> <br/>codec?}
43+
ValidateDNSLink{Is <code>id</code> <br/> a DNSLink name <br/> with TXT record: <br/> <code>dnslink=/ipfs/</code><br/> or <code>=/ipns/</code>}
44+
45+
IsIpfsOrIpns --->|/ipfs/id/..| ValidateRootCID
46+
IsIpfsOrIpns -->|/ipns/id/..| ValidateIPNSKey
47+
ValidateIPNSKey -->|NO| ValidateDNSLink
48+
end
49+
50+
subgraph FoundValidIPFSContent [Confirmed we've found a Valid IPFS Resource]
51+
style FoundValidIPFSContent margin-top: 50
52+
ValidIPFSCID[Valid /ipfs/cid/.. <br/>Content Path]
53+
ValidIPNSKey[Valid /ipns/key/.. <br/>Content Path]
54+
ValidDNSLink[Valid /ipns/dnslink/.. <br/>Content Path]
55+
56+
ValidateRootCID ===>|YES, /ipfs/cid/..| ValidIPFSCID
57+
ValidateIPNSKey ===>|YES, /ipns/key/..| ValidIPNSKey
58+
ValidateDNSLink ===>|YES, /ipfs/dnslink/..| ValidDNSLink
59+
end
60+
61+
subgraph NonGwURLToPath [See if non-Gateway URL has a Content Path]
62+
IsCachedDNSLink{Does <br/> <code>URL.hostname</code> <br/> match a cached <br/> DNSLink name?}
63+
IsHeaderPresent{Is <code>X-Ipfs-Path</code> <br/> header present?}
64+
IsError{Did request fail? <br/> HTTP error>500 <br/> or network error}
65+
IsDNSLinkAtHostname{Does DNSLink exists <br/> for <code>URL.hostname</code>?<br/>}
66+
PathFromHeader[Read value from <code>X-Ipfs-Path</code>]
67+
end
68+
69+
IsGateway -->|NO| IsCachedDNSLink
70+
IsCachedDNSLink ==>|YES| ValidDNSLink
71+
IsCachedDNSLink -->|NO| IsHeaderPresent
72+
IsHeaderPresent ==>|YES| IsDNSLinkAtHostname
73+
IsHeaderPresent -->|NO| IsError
74+
IsError ==>|YES| IsDNSLinkAtHostname
75+
IsDNSLinkAtHostname ==>|YES| ValidDNSLink
76+
IsDNSLinkAtHostname -->|NO| PathFromHeader
77+
PathFromHeader --> PotentialContentPath
78+
```
79+
80+
## What to do with detected paths?
81+
82+
### Immutable `/ipfs/cid/..`
83+
84+
- Display "Open via IPFS" button in UI
85+
- Clicking it should open `ipfs://cid/path?query#hash` (preserving any `?query` or `#hash` from the original HTTP URL)
86+
- If "IPFS Gateway Redirect / Protocol Upgrade" feature is enabled, and the HTTP URL was a gateway one, redirect automatically to `ipfs://cid/path?query#hash`
87+
88+
### Mutable `/ipns/key/..`
89+
- Display "Open via IPFS" button in UI
90+
- Clicking it should open `ipns://key/path?query#hash` (preserving any `?query` or `#hash` from the original HTTP URL)
91+
- If "IPFS Gateway Redirect / Protocol Upgrade" is enabled, and the original HTTP URL was a gateway one, redirect automatically to `ipns://dnslink/path?query#hash`
92+
93+
### Mutable `/ipns/dnslink/..`
94+
95+
- Display "Open via IPFS" button in UI
96+
- Clicking it should open `ipns://dnslink/path?query#hash` (preserving `?query` or `#hash` from the original HTTP URL)
97+
- If "DNSLink Website Redirect / Protocol Upgrade" is enabled, redirect automatically to `ipns://dnslink/path?query#hash`
98+
- It is a good practice to internally cache the fact that domain has a valid DNSLink.
99+
- TTL from TXT record can be used as a hint when to expire cache entry.
100+
- Performance can be improved further by using cached flag and revalidating it asynchronously, without blocking browser navigation.
101+
102+
## FAQ
103+
104+
- What if a browser does not support `ipfs://` and `ipns://` natively?
105+
- Implementation can use a [HTTP Gateway](/reference/http/gateway/) as a fallback: convert them to `//gatewayhost/ipxs/id/..` paths, or leverage the built-in URI router at `//gatewayhost/ipfs/?uri=%s`
106+
- Why should `?query` or `#hash` from the original HTTP URL be preserved?
107+
- Link could point at specific `#section` of a longer article. It is also common for JS running on a page to use `?query` and `#hash` for navigation and ad-hoc storage for some state.
108+
- Should user agent redirect when URL does not match gateway convention, `URL.hostname` does not have a valid DNSLink, but `X-Ipfs-Path` is present in HTTP response AND points at immutable `/ipfs/cid`?
109+
- This is an edge case, and we've seen that it is often a misconfiguration caused by invalid/missing DNSLink that could lead to bad UX when automatic redirect is involved: user ends up on immutable copy of a website, bookmark it or keep tab open, and miss updates when DNSLink setup is fixed.
110+
- It is suggested to not redirect this edge case, or provide a setting that controls this type of redirect. Usually, showing "Open via IPFS" in the user interface is enough for this case.
111+
112+
## Implementation examples
113+
114+
### Brave
115+
116+
[IPFS is supported in Brave since 2021](https://brave.com/brave-integrates-ipfs/). Current features include `ipfs://` and `ipns://` URI support, ability to resolve them using a public or local gateway, opt-in Gateway and DNSLink redirects, and "Open via IPFS" button in the address bar:
117+
118+
> ![Open via IPFS in Brave addressbar](https://user-images.githubusercontent.com/157609/110859368-9a0d7300-82bb-11eb-934d-4e38718dbacb.png)
119+
120+
### IPFS Companion
121+
122+
Firefox and Chromium-based browsers such as Google Chrome, Microsoft Edge can be augumented with [IPFS Companion](/install/ipfs-companion/) browser extension.
123+
124+
It provides detection of IPFS resources, redirect functionality, and a handy UI:
125+
126+
> ![Quick runthrough of basic IPFS Companion features](https://gateway.ipfs.io/ipfs/QmSsGphTN1eWMhkFFNFb23jWTXyhNbo47PF9FbmC6ZaRNg)
127+

0 commit comments

Comments
 (0)