Make nns/gudz close to RFC #114
4 changed files with 345 additions and 0 deletions
160
docs/globally-unique-domain-zone.md
Normal file
160
docs/globally-unique-domain-zone.md
Normal file
|
@ -0,0 +1,160 @@
|
|||
# Globally unique domain zone
|
||||
|
||||
**Make sure you understand the [basic concepts](../nns/README.md) of `NNS`.**
|
||||
|
||||
`Globally Unique Domains Zone` (`GUDZ`) is an extension of `NNS` that ensures unique names across multiple domain zones. When this option is enabled, all newly created domains will automatically receive a corresponding alias in the designated global zone. Deleting a domain will also remove its alias from the global zone.
|
||||
|
||||
It's important to note that this feature is not retroactive: domains created before this option is enabled will not receive a global alias. Likewise, if the option is later disabled, domains that already have a `GUDZ` alias will retain their records. To fully disable `GUDZ`, all domains must be recreated with the option turned off.
|
||||
|
||||
To enable `GUDZ`, add a `cnametgt=$(global domain)` `TXT` record that specifies the global zone.
|
||||
|
||||
**Example:**
|
||||
|
||||
Domains:
|
||||
- `poland`
|
||||
- `sweden`
|
||||
- `animals.org`
|
||||
|
||||
It is necessary to associate the domain zones `.poland` and `.sweden` into the global zone `.animals`.
|
||||
|
||||
![](img/GUDZ.png)
|
||||
|
||||
Create domains:
|
||||
|
||||
```
|
||||
frostfs-adm morph nns register --name="poland" --email="email@email.email"
|
||||
frostfs-adm morph nns register --name="sweden" --email="email@email.email"
|
||||
frostfs-adm morph nns register --name="org" --email="email@email.email"
|
||||
frostfs-adm morph nns register --name="animals.org" --email="email@email.email"
|
||||
```
|
||||
|
||||
Add the `cnametgt` records:
|
||||
|
||||
```
|
||||
frostfs-adm morph nns add-record --name="poland" --data="cnametgt=animals.org" --type="txt"
|
||||
frostfs-adm morph nns add-record --name="sweden" --data="cnametgt=animals.org" --type="txt"
|
||||
```
|
||||
|
||||
Create a domain with mapping to the global zone:
|
||||
|
||||
```
|
||||
frostfs-adm morph nns register --name="bober.poland" --email="email@email.email"
|
||||
```
|
||||
|
||||
Add any `TXT` record
|
||||
|
||||
```
|
||||
frostfs-adm morph nns add-record --name="bober.poland" --data="CID" --type="txt"
|
||||
```
|
||||
|
||||
Verify that the created domain has alias in the global zone
|
||||
|
||||
```
|
||||
frostfs-adm morph nns tokens -v
|
||||
|
||||
balance.frostfs
|
||||
animals.org
|
||||
group.frostfs
|
||||
container
|
||||
org
|
||||
container.frostfs
|
||||
proxy.frostfs
|
||||
policy.frostfs
|
||||
alphabet0.frostfs
|
||||
sweden
|
||||
frostfsid.frostfs
|
||||
bober.animals.org (CNAME: bober.poland)
|
||||
aarifullin marked this conversation as resolved
Outdated
|
||||
netmap.frostfs
|
||||
frostfs
|
||||
poland
|
||||
bober.poland
|
||||
```
|
||||
|
||||
Create of a conflicting domain
|
||||
```
|
||||
frostfs-adm morph nns register --name="bober.sweden" --email="email@email.email"
|
||||
|
||||
unable to register domain: script failed (FAULT state) due to an error: at instruction 1263 (THROW): unhandled exception: "global domain is already taken: bober.animals.org. Domain: bober.poland
|
||||
```
|
||||
|
||||
**Disable GUDZ**
|
||||
Delete `cnametgt` records
|
||||
|
||||
```
|
||||
frostfs-adm morph nns delete-records --type=txt --name=poland
|
||||
```
|
||||
Create `hamster.poland` and `hamster.sweden`
|
||||
```
|
||||
frostfs-adm morph nns register --name="hamster.poland" --email="email@email.email"
|
||||
frostfs-adm morph nns register --name="hamster.sweden" --email="email@email.email"
|
||||
```
|
||||
`hamster.poland` and `hamster.sweden` does not have alias
|
||||
```
|
||||
frostfs-adm morph nns tokens -v
|
||||
balance.frostfs
|
||||
animals.org
|
||||
group.frostfs
|
||||
container
|
||||
org
|
||||
container.frostfs
|
||||
proxy.frostfs
|
||||
policy.frostfs
|
||||
alphabet0.frostfs
|
||||
sweden
|
||||
frostfsid.frostfs
|
||||
bober.animals.org (CNAME: bober.poland)
|
||||
netmap.frostfs
|
||||
frostfs
|
||||
poland
|
||||
bober.poland
|
||||
hamster.poland
|
||||
```
|
||||
Delete global alias of `bober.poland`
|
||||
|
||||
```
|
||||
frostfs-adm morph nns delete-records --name="bober.poland" --type="txt"
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
frostfs-adm morph nns tokens -v
|
||||
balance.frostfs
|
||||
animals.org
|
||||
group.frostfs
|
||||
container
|
||||
org
|
||||
container.frostfs
|
||||
proxy.frostfs
|
||||
policy.frostfs
|
||||
alphabet0.frostfs
|
||||
sweden
|
||||
frostfsid.frostfs
|
||||
netmap.frostfs
|
||||
frostfs
|
||||
poland
|
||||
bober.poland
|
||||
hamster.poland
|
||||
```
|
||||
Delete `bober.poland`
|
||||
```
|
||||
frostfs-adm morph nns delete --name="bober.poland"
|
||||
```
|
||||
|
||||
```
|
||||
frostfs-adm morph nns tokens -v
|
||||
balance.frostfs
|
||||
animals.org
|
||||
group.frostfs
|
||||
container
|
||||
org
|
||||
container.frostfs
|
||||
proxy.frostfs
|
||||
policy.frostfs
|
||||
alphabet0.frostfs
|
||||
sweden
|
||||
frostfsid.frostfs
|
||||
netmap.frostfs
|
||||
frostfs
|
||||
poland
|
||||
hamster.poland
|
||||
```
|
139
docs/img/GUDZ.drawio
Normal file
139
docs/img/GUDZ.drawio
Normal file
|
@ -0,0 +1,139 @@
|
|||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0" version="24.7.14">
|
||||
<diagram name="Page-1" id="N1NjK5oQ_tQiBXsDL3WT">
|
||||
<mxGraphModel dx="989" dy="917" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-1" value=".ns" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="130" y="160" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-2" value=".org" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="160" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-3" value=".sweden" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" y="280" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-4" value=".poland" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="10" y="280" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-1" target="l_qLPBR5nCWAv8Wbn5Wl-4">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="260" y="480" as="sourcePoint" />
|
||||
<mxPoint x="310" y="430" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-1" target="l_qLPBR5nCWAv8Wbn5Wl-3">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="200" y="230" as="sourcePoint" />
|
||||
<mxPoint x="80" y="290" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-7" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="10" y="400" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-8" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-4" target="l_qLPBR5nCWAv8Wbn5Wl-7">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="190" y="450" as="sourcePoint" />
|
||||
<mxPoint x="70" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-9" value="bober" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;strokeColor=#FF6666;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" y="400" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-10" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;fillColor=#f8cecc;strokeColor=#FF6666;" edge="1" parent="1" target="l_qLPBR5nCWAv8Wbn5Wl-9">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="310" y="340" as="sourcePoint" />
|
||||
<mxPoint x="310" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-11" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="400" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-12" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" target="l_qLPBR5nCWAv8Wbn5Wl-11">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="500" y="340" as="sourcePoint" />
|
||||
<mxPoint x="500" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-13" value="<font style="font-size: 5px;">TXT cnametgt=animals.org</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
|
||||
<mxGeometry x="30" y="310" width="80" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-14" value=".<span lang="en" class="Y2IQFc">animals</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="280" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-15" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="499.76" y="220" as="sourcePoint" />
|
||||
<mxPoint x="499.76" y="280" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-16" value="<font style="font-size: 5px;">TXT cnametgt=</font><font style="font-size: 5px;">animals.org</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
|
||||
<mxGeometry x="270" y="310" width="80" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-17" value="" style="shape=umlDestroy;whiteSpace=wrap;html=1;strokeWidth=3;targetShapes=umlLifeline;strokeColor=#FF6666;" vertex="1" parent="1">
|
||||
<mxGeometry x="292.5" y="350" width="35" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-18" value="<span style="white-space: pre-wrap;" data-src-align="0:10" class="EzKURWReUAB5oZgtQNkl">[ global</span><span style="white-space: pre-wrap;"> </span><span style="white-space: pre-wrap;" data-src-align="11:8" class="EzKURWReUAB5oZgtQNkl">domain</span><span style="white-space: pre-wrap;"> </span><span style="white-space: pre-wrap;" data-src-align="20:4" class="EzKURWReUAB5oZgtQNkl">zone ]</span>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="425" y="100" width="150" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-19" value="<font style="font-size: 5px;">CNAME bober</font><font style="font-size: 5px;">.</font><font style="font-size: 5px;">poland</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
|
||||
<mxGeometry x="465" y="430" width="70" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-1" value=".ns" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="130" y="-240" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-2" value=".org" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="-240" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-3" value=".sweden" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" y="-120" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-4" value=".poland" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="10" y="-120" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-1" target="Z9nLDmrU-mAa8Hye4rHX-4">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="260" y="80" as="sourcePoint" />
|
||||
<mxPoint x="310" y="30" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-1" target="Z9nLDmrU-mAa8Hye4rHX-3">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="200" y="-170" as="sourcePoint" />
|
||||
<mxPoint x="80" y="-110" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-7" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="10" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-8" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-4" target="Z9nLDmrU-mAa8Hye4rHX-7">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="190" y="50" as="sourcePoint" />
|
||||
<mxPoint x="70" y="-10" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-9" value=".<span lang="en" class="Y2IQFc">animals</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="-120" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-10" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="499.76" y="-180" as="sourcePoint" />
|
||||
<mxPoint x="499.76" y="-120" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-11" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="309.71000000000004" y="-60" as="sourcePoint" />
|
||||
<mxPoint x="309.71000000000004" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-12" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" width="120" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-13" value="<span style="white-space: pre-wrap;" data-src-align="0:10" class="EzKURWReUAB5oZgtQNkl">[ without global</span><span style="white-space: pre-wrap;"> </span><span style="white-space: pre-wrap;" data-src-align="11:8" class="EzKURWReUAB5oZgtQNkl">domain</span><span style="white-space: pre-wrap;"> </span><span style="white-space: pre-wrap;" data-src-align="20:4" class="EzKURWReUAB5oZgtQNkl">zone ]</span>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="390" y="-300" width="180" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
BIN
docs/img/GUDZ.png
Normal file
BIN
docs/img/GUDZ.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
46
nns/README.md
Normal file
46
nns/README.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# NNS
|
||||
NNS - Neo Name Service is a service that allows manage a domain name as a digital asset (NFT). It has an interface similar to `DNS` but has significant differences in its internal structure.
|
||||
|
||||
## Entities:
|
||||
|
||||
- Domain
|
||||
- Record
|
||||
- Owner
|
||||
- Committee
|
||||
|
||||
### Domain
|
||||
|
||||
Domain is string that satisfies the following requirements:
|
||||
- Length from 2 to 255 characters.
|
||||
- Root domain must start with a letter.
|
||||
- All other fragments must start and end with a letter or digit.
|
||||
|
||||
Domain has owner, a registration period, and may optionally have records.
|
||||
|
||||
A fee established by the committee is charged upon domain registration. After registration, the owner can manage this asset (add/delete records, transfer ownership to another owner) until the end of the domain registration period.
|
||||
|
||||
### Record
|
||||
|
||||
A record is a pair of values `<type, string>`.
|
||||
|
||||
Supported record types:
|
||||
|
||||
| Type | Description |
|
||||
|-------|-------------------------------------------|
|
||||
| A | Represents address record type |
|
||||
| AAA | Represents IPv6 address record type |
|
||||
| TXT | Represents text record type |
|
||||
| CNAME | Represents canonical name record type |
|
||||
| SOA | Represents start of authority record type |
|
||||
|
||||
### Owner
|
||||
|
||||
An owner is a wallet that has the right to manage this NFT (domain).
|
||||
|
||||
### Committee
|
||||
|
||||
The committee makes new tokens (domains), sets, and charges a fee for issuance.
|
||||
|
||||
## Globally Unique Domain Zone
|
||||
|
||||
For more information, see [here](../docs/globally-unique-domain-zone.md).
|
Loading…
Reference in a new issue
Why
bober.animals.org
is associated withbober.sweden
but not.poland
if?
Fixed typo