Nom Payment Portal API Spec
===
###### tags: `Technical Specification`
# General Note
- [Nom Payment Portal API Github Repository](https://github.com/collectiveactions/nom-payment-portal-api)
- This document describes the specification for the Nom Payment Portal API that serves as the temporary API sets for storing namespace, name types, and names purchased by the end user of Nom
- The purpose of this document is to make clear all the validations that are required in order to not break the data integrity of the said entities (Namespace, Nametype, Names) while they are not yet blessed by the Data Integrity of Holochain.
# Specification
## Deployment
- This set of APIs are deployed using Cloudflare Workers
## Storage
- We are using Cloudflare's `Durable Object` store.
- the `Durable Object` stores all namespaces and names in a path structure that matches the path structure we will have in Holochain (see below)
- the Namespace ID is generated using the Durable Object API `newUniqueId` which guarantees uniqueness of data in the same namespace of Object. Read more [here](https://developers.cloudflare.com/workers/runtime-apis/durable-objects/#generating-ids-randomly)
- The actual values of the `Namespace` details are stored in the Durable Object. This is to ensure that writes on the same Namespace have strong consistency. Read more [here](https://developers.cloudflare.com/workers/learning/how-kv-works/)
## Durable Object Store Path Structure
- Some of the types here are yet to be defined
- Types used here are defined [here](#Types-and-Validation)
```json
// Nom Registry Durable Object
// key glossary
// {customerPK} - public key of customer generated by Chaperone
// {arthur} - an example global agent name purchased by a customer
// {holochain} - an example global org name purchased by a customer
// {namespaceuuid} - temporary namespace uuid
// {username} - the path of a name type
// {meister} - an example namespac bound name purchased by a customer
{
// WITNESS
"witness/{customerPK}": WitnessRegistration, // information of the witness registering
// GLOBAL NAMES
"nomnom/{arthur}": NomNomRegistration // the details of the global agent name purchased
"orgnom/{holochain}": OrgNomRegistration
// LOCAL NAMES
"registration/{namespace_uuid}/{agentname}/{meister}": NameRegistration // name registered for a particular name type in a namespace
// NAMESPACE
"customer/namespace/{customerPK}": namespaceId[], // all namespaces a customer has bought. Used for query
"namespace/{namespace_uuid}/details": Namespace,
"namespace/{namespace_uuid}/properties": NamespaceProperties,
"nametypes/{namespace_uuid}/{agentname}/details": NameType,
}
// This file structure is based on this file structure initially suggested
~/nom_registry
./witnesses
./witness_profile.json
./global_names
./gusername1.json
./gusername2.json
./namespaces
./app_namespace_index
./namespace_properties.json
./username
./username1.json
./username2.json
./username3.json
./other_name_type
./name1.json
```
## APIs
- base url: api.nomde.net
### Creates
#### **Register Customer(User)**
- URI: `.post(customers/{id})`
- input
```typescript
// from URL
// customer {id} - the public key of the API caller in Base64 string
// output
export interface RegisterCustomerResponse {
success: boolean,
pubKey: string
}
// sample output
{
success: true,
pubKey: "uchA83710knldngalweghowahanflfew2"
}
```
#### **create namespace**
- URI: `.post('customers/{id}/namespaces')`
- input
```typescript
// from url
// customers {id} - the public key of the api caller generated by Chaperone
// request body
export type CreateNamespaceInput = {
NomNomRegOptIn: boolean; // this is the global name scope for agents
OrgNomRegOptIn: boolean; // this is the global name scope for organizations
bindingPublicKey: string;
properties: CreateNamespaceProperties;
signature: string; // signature to this input that can be verified with the public key
token: string; // token that is provided as a proof of payment by the payment gateway
};
export type CreateNamespaceProperties = {
displayName: string;
dnaVersions: string[];
orgDetail: Organization;
nameTypes: CreateNameTypeInput[];
nomNomNameType?: string; // path of the name type for agent in nameTypes array
orgNomNameType?: string; // path of the name type for organization in nameTypes array
maintainers: string[]; // Agent public keys of maintainers. Set by the owner of namespace.
marketingInfo: MarketingInfo;
ownershipProof: string; // Option<Signature>. Signature of the owner of namespace.
};
type CreateNameTypeInput = {
path: string; // immutable(permanent) name of the name type
validNameStructureRegex: string;
reservedNames: string[];
displayName: string;
ttl: number; // u8
purchasePrice: number; // u32 (in USD cents)
prePurchasedNameAllotments: CreatePrePurchasedNameAllotment[]; // can only be added. Delete is not allowed.
signature: string; // signature to this input that can be verified with the public key
binding: NameTypeBinding;
};
type CreatePrePurchasedNameAllotmentInput = {
nameTypePath: string;
qty: number; // bundle of free names purchased
};
enum NameTypeBinding {
NomNoms = "NomNoms",
OrgNoms = "OrgNoms",
NoBinding = "NoBinding",
}
type Organization = {
name: string;
email: string;
country: string;
province: string;
city: string;
contactNo: string;
};
type MarketingInfo = {
logo?: string; // base64 of logo
appName: string;
description?: string;
appStatus: AppStatus;
};
enum AppStatus {
Development = "Development",
Alpha = "Alpha",
Beta = "Beta",
}
// Sample Input:
const sampleInputData = {
nomNomRegOptIn: false,
orgNomRegOptIn: false,
bindingPublicKey: "samplePubKey",
properties: {
displayName: "sampleDisplayName",
dnaVersions: ["sampleDnaVersion"],
orgDetail: {
name: "sampleOrgName",
email: "sample@email.com",
country: "sampleOrgCountry",
contactNo: "sampleOrgContactNo",
city: "sampleOrgCity",
province: "sampleOrgProvince",
},
nameTypes: [
{
path: "sampleNameTypePath",
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["sampleReservedName"],
displayName: "sampleNameTypeDisplayName",
ttl: 1,
purchasePrice: 100,
prePurchasedNameAllotments: [
{
nameTypePath: "sampleNameTypePath",
qty: 1000,
},
],
binding: "NoBinding",
},
],
maintainers: ["sampleMaintainer"],
marketingInfo: {
appName: "sampleDescription",
appStatus: "Alpha",
},
ownershipProof: "sampleOwnershipProof",
},
signature: "sampleSignature",
token: "sampleToken",
};
// output
interface CreateNamespaceResponse {
success: boolean;
data: {
namespace: Namespace;
properties: NamespaceProperties;
nameTypes: {
[key: string]: NameType;
};
};
}
// Sample Output:
{
success: boolean;
data: {
namespace: {
nomNomRegOptIn: false;
orgNomRegOptIn: false;
ttl: 1;
bindingPublicKey: "uchcAGeahgoowaugtnlekwnt!@3H#T2ipqtn";
};
properties: {
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
displayName: "displayName";
dnaVersions: ["uchgfaewukbglaewjbg", "ahgeeohgboawlbnwglkbga"];
orgDetail: {
name: "sampleOrgName",
email: "sample@email.com",
country: "sampleOrgCountry",
contactNo: "sampleOrgContactNo",
city: "sampleOrgCity",
province: "sampleOrgProvince",
};
nameTypes: {
"sampleNameTypePath": "sampleNameTypePath"
"sampleNameTypePath2": "sampleNameTypePath2"
};
nomNomNameType?: "sampleNameTypePath";
orgNomNameType?: "sampleNameTypePath2";
maintainers: ["ucugblewabglknek;aw", "aehwgubawelgblaewknglkaw"];
marketingInfo: {
logo?: "geawgewajgnwajlnglawegewa";
appName: "sampleAppName";
appStatus: "Development";
description: "sample description"
};
ownershipProof: "aebgfiaewgjbawejlbgjkwebgjlaew";
};
nameTypes: {
"sampleNameTypePath": {
path: "sampleNameTypePath",
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["admin", "org"];
displayName: "username";
ttl: 3;
purchasePrice: 100; // in cents
prePurchasedNameAllotments: [{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
}]
};
};
};
}
```
- Required Validation:
- `Namespace: Create`
- `NamespaceProperties: Create`
- `NameType: Create`
#### **create(add) nametype**
- URI: `.post('customers/{id}/namespaces/{id}/nametype')`
- input
```typescript
// From url
// customers {id} - the public key of the api caller generated by Chaperone
// namespace {id} - a random, unique id generated by Cloudflare's Transaction Storage API
enum NameTypeBinding = {
NomNoms,
OrgNoms,
NoBinding
}
export type CreateNameTypeInput = {
namespace: string, // namespace ID
path: string; // immutable(permanent) name of the name type
validNameStructureRegex: string;
reservedNames: string[];
displayName: string;
ttl: number; // u8
purchasePrice: number; // u32 (in USD cents)
prePurchasedNameAllotments: CreatePrePurchasedNameAllotmentInput[]; // can only be added. Delete is not allowed.
signature: string; // signature to this input that can be verified with the public key
binding: NameTypeBinding;
};
export type CreatePrePurchasedNameAllotmentInput = {
nameTypePath: string;
qty: number; // bundle of free names purchased
};
// sample input
const createNameTypeInput: CreateNameTypeInput = {
namespace: "namespaceId",
signature: "sampleSignature",
path: "sampleNameTypePath2",
validNameStructureRegex: "/[abc]+/g",
reservedNames: ["sampleReservedName"],
displayName: "sampleNameTypeDisplayName",
ttl: 1,
purchasePrice: 0,
prePurchasedNameAllotments: [
{
nameTypePath: "sampleNameTypePath2",
qty: 1000,
},
],
binding: "NoBinding",
};
// sample output
CreateNameTypeResponse {
success: true;
data: {
path: "sampleNameTypePath",
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["admin", "org"];
displayName: "username";
ttl: 3;
purchasePrice: 100; // in cents
prePurchasedNameAllotments: [{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
}]
};
}
```
- Required Valdation:
- `Nametype: Create`
- `NamespaceProperties: Update`- For updating `Nametypes`
#### **create(add) pre purchased names for nametype**
- URI: `.put('customers/{id}/namespaces/nametype/{id}/pre-purchased-names')`
- input
```typescript
// from url
// customer {id} - public key of the caller provided by Chaperone
// nametype {id} - path of the name type
type PrePurchasedNameAllotment = {
namespace: string;
nameTypePath: string;
qty: number; // bundle of free names purchased
};
type AddPrePurchasedNameAllotmentInput = PrePurchasedNameAllotment & {
signature?: string; // signature to this input that can be verified with the public key
token?: string; // token that is provided as a proof of payment by the payment gateway
};
// sample input
{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
signature: "sampleSignature",
token: "sampleToken"
}
// sample output
CreateNameTypeResponse {
success: true;
data: {
path: "sampleNameTypePath",
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["admin", "org"];
displayName: "username";
ttl: 3;
purchasePrice: 100; // in cents
prePurchasedNameAllotments: [{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
}]
};
}
```
- Required Validation:
- `Nametype: Update` - for `PrePurchasedNameAllotment`
### Updates
#### **update namespace properties**
- URI: `.put('/customers/{id}/namespaces/{id}/property')`
- input
```typescript
// from url
// customer {id} - public key of the caller provided by Chaperone
// namespace {id} - uuid of the namespace
type updateNamespacePropertiesInput = {
displayName: string;
dnaVersions: string[];
orgDetail: Organization;
maintainers: string[];
signature: Signature; // signature to this input that can be verified with the public key
}
// sample input
{
displayName: "sampleDisplayName2",
dnaVersions: ["sampleDnaVersion2", "sampleDnaVersion3"],
orgDetail: {
name: "sampleOrgName2",
email: "sampleOrgEmail2",
country: "sampleOrgCountry2",
contactNo: "sampleOrgContactNo2",
city: "sampleOrgCity2",
province: "sampleOrgProvince2",
},
maintainers: ["sampleMaintainer2", "sampleMaintainer3"],
signature: "sampleSignature",
};
// sample output
{
success: true;
data: {
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
displayName: "displayName";
dnaVersions: ["uchgfaewukbglaewjbg", "ahgeeohgboawlbnwglkbga"];
orgDetail: {
name: "sampleOrgName",
email: "sample@email.com",
country: "sampleOrgCountry",
contactNo: "sampleOrgContactNo",
city: "sampleOrgCity",
province: "sampleOrgProvince",
};
nameTypes: {
"sampleNameTypePath": "sampleNameTypePath"
"sampleNameTypePath2": "sampleNameTypePath2"
};
nomNomNameType?: "sampleNameTypePath";
orgNomNameType?: "sampleNameTypePath2";
maintainers: ["ucugblewabglknek;aw", "aehwgubawelgblaewknglkaw"];
marketingInfo: {
logo?: "geawgewajgnwajlnglawegewa";
appName: "sampleAppName";
appStatus: "Development";
description: "sample description"
};
ownershipProof: "aebgfiaewgjbawejlbgjkwebgjlaew";
}
}
```
- Required Validation:
- `NamespaceProperties: Update` - For updating `NamespaceProperties`
#### **update marketing info**
- URI: `.put('/customers/{id}/namespaces/{id}/marketing-info')`
- input
```typescript
// from url
// customer {id} - public key of the caller provided by Chaperone
// namespace {id} - uuid of the namespace
type MarketingInfo = {
logo?: string; // base64 of logo
appName: string;
appStatus: AppStatus;
description?: string
};
type updateMarketingInfo = MarketingInfo & {
signature: Signature; // signature to this input that can be verified with the public key
}
// sample input
{
appName: "sampleDescription2",
appStatus: "Beta",
signature: "sampleSignature",
}
// sample output
{
success: true;
data: {
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
displayName: "displayName";
dnaVersions: ["uchgfaewukbglaewjbg", "ahgeeohgboawlbnwglkbga"];
orgDetail: {
name: "sampleOrgName",
email: "sample@email.com",
country: "sampleOrgCountry",
contactNo: "sampleOrgContactNo",
city: "sampleOrgCity",
province: "sampleOrgProvince",
};
nameTypes: {
"sampleNameTypePath": "sampleNameTypePath"
"sampleNameTypePath2": "sampleNameTypePath2"
};
nomNomNameType?: "sampleNameTypePath";
orgNomNameType?: "sampleNameTypePath2";
maintainers: ["ucugblewabglknek;aw", "aehwgubawelgblaewknglkaw"];
marketingInfo: {
logo?: "geawgewajgnwajlnglawegewa";
appName: "sampleAppName";
appStatus: "Development";
description: "sample description"
};
ownershipProof: "aebgfiaewgjbawejlbgjkwebgjlaew";
};
}
```
- Required Validation:
- `NamespaceProperties: Update` - For updating `MarketingInfo`
#### **update nametype**
- URI: `.put('customers/{id}/namespaces/{id}/nametype/{id}')`
- input
```typescript
// from url
// customer {id} - public key of the caller provided by Chaperone
// namespace {id} - uuid of the namespace
// nametype {id} - path of the name type
type updateNameTypeInput = {
displayName: string;
ttl: number; // u8
purchasePrice: number; // u32 (in USD cents)
signature: Signature; // signature to this input that can be verified with the public key
}
// sample input
{
signature: "sampleSignature",
displayName: "sampleNameTypeDisplayName2",
ttl: 3,
purchasePrice: 0,
};
// sample output
{
success: true;
data: {
path: "sampleNameTypePath",
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["admin", "org"];
displayName: "username";
ttl: 3;
purchasePrice: 100; // in cents
prePurchasedNameAllotments: [{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
}]
};
}
```
- Required Validation:
- `NameType: Update`
### Reads
#### Login API
- URI: `.get('/customers/{id})`
```typescript
// output type
export interface AmIRegisteredResponse {
registered: boolean,
pubKey: string
}
// sample output
{
registered: true,
pubKey: "uchae284bglsflekg-gehaowengla"
}
{
registered: false,
}
```
#### get all namespaces
- URI: `.get('/namespaces')`
```typescript
// sample output
{
"sucess": true,
"data":[
{
"namespace": {
"nomNomRegOptIn":false,
"orgNomRegOptIn":false,
"ttl":1,
"bindingPublicKey": "samplePubKey"
},
"properties":
{
"namespace":
"044ad94a76c1808c01bc210027ae340e5aee081677f8c04573bd7ce8e8c461d2",
"displayName": "sampleDisplayName",
"dnaVersions": ["sampleDnaVersion"],
"orgDetail":
{
"name": "sampleOrgName",
"email":"sample@email.com",
"country": "sampleOrgCountry",
"contactNo": "sampleOrgContactNo",
"city":"sampleOrgCity",
"province": "sampleOrgProvince"
},
"nameTypes":
{
"sampleNameTypePath": "sampleNameTypePath"
},
"maintainers": ["sampleMaintainer"],
"marketingInfo": {
"logo": "awebgewbglkaewg"
"appName":"sampleDescription",
"appStatus":"Alpha",
"description": "sampleDescription"
},
"ownershipProof": "sampleOwnershipProof"
},
"nameTypes": {
"sampleNameTypePath":
{
"path": "sampleNameTypePath",
"namespace": "044ad94a76c1808c01bc210027ae340e5aee081677f8c04573bd7ce8e8c461d2",
"validNameStructureRegex": "/[abc]dd+/g",
"reservedNames":["sampleReservedName"],
"displayName":"sampleNameTypeDisplayName",
"ttl":1,
"purchasePrice":100,
"prePurchasedNameAllotments": []
}
}
}
]
}
```
#### get all my namespaces
- URI: `.get('customers/{id}/namespaces'`
- `customers id: public key of the caller provided by Chaperone`
```typescript
{
"sucess": true,
"data":[
{
"namespace": {
"nomNomRegOptIn":false,
"orgNomRegOptIn":false,
"ttl":1,
"bindingPublicKey": "samplePubKey"
},
"properties":
{
"namespace":
"044ad94a76c1808c01bc210027ae340e5aee081677f8c04573bd7ce8e8c461d2",
"displayName": "sampleDisplayName",
"dnaVersions": ["sampleDnaVersion"],
"orgDetail":
{
"name": "sampleOrgName",
"email":"sample@email.com",
"country": "sampleOrgCountry",
"contactNo": "sampleOrgContactNo",
"city":"sampleOrgCity",
"province": "sampleOrgProvince"
},
"nameTypes":
{
"sampleNameTypePath": "sampleNameTypePath"
},
"maintainers": ["sampleMaintainer"],
"marketingInfo": {
"logo": "awebgewbglkaewg"
"appName":"sampleDescription",
"appStatus":"Alpha",
"description": "sampleDescription"
},
"ownershipProof": "sampleOwnershipProof"
},
"nameTypes": {
"sampleNameTypePath":
{
"path": "sampleNameTypePath",
"namespace": "044ad94a76c1808c01bc210027ae340e5aee081677f8c04573bd7ce8e8c461d2",
"validNameStructureRegex": "/[abc]dd+/g",
"reservedNames":["sampleReservedName"],
"displayName":"sampleNameTypeDisplayName",
"ttl":1,
"purchasePrice":100,
"prePurchasedNameAllotments": []
}
}
}
]
}
```
#### get namespace
- URI: `.get('namespaces/{id}'`
- `namespace id: id of the namespace`
```typescript
{
success: true;
data: {
namespace: {
nomNomRegOptIn: false;
orgNomRegOptIn: false;
ttl: 1;
bindingPublicKey: "uchcAGeahgoowaugtnlekwnt!@3H#T2ipqtn";
};
properties: {
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
displayName: "displayName";
dnaVersions: ["uchgfaewukbglaewjbg", "ahgeeohgboawlbnwglkbga"];
orgDetail: {
name: "sampleOrgName",
email: "sample@email.com",
country: "sampleOrgCountry",
contactNo: "sampleOrgContactNo",
city: "sampleOrgCity",
province: "sampleOrgProvince",
};
nameTypes: {
"sampleNameTypePath": "sampleNameTypePath"
"sampleNameTypePath2": "sampleNameTypePath2"
};
nomNomNameType?: "sampleNameTypePath";
orgNomNameType?: "sampleNameTypePath2";
maintainers: ["ucugblewabglknek;aw", "aehwgubawelgblaewknglkaw"];
marketingInfo: {
logo?: "geawgewajgnwajlnglawegewa";
appName: "sampleAppName";
appStatus: "Development";
description: "sample description"
};
ownershipProof: "aebgfiaewgjbawejlbgjkwebgjlaew";
};
nameTypes: {
"sampleNameTypePath": {
path: "sampleNameTypePath",
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
validNameStructureRegex: "/[abc]dd+/g",
reservedNames: ["admin", "org"];
displayName: "username";
ttl: 3;
purchasePrice: 100; // in cents
prePurchasedNameAllotments: [{
namespace: "305q295892q3ytqiohwhn3tognlewanfe";
nameTypePath: "sampleNameTypePath";
qty: 10000;
}]
};
};
};
}
```
#### *...add more here as needed*
## Types and Validation
- Types used in the API
### `Namespace`
#### validation
- Create
- Check that required fields exist in input
- `Namespace.NomNomRegOptIn`
- `Namespace.OrgNomRegOptIn`
- `Namespace.bindingPublicKey`
- `Namespace.properties`
- check the signature that is in the request and verify it with the binding public key
- sign the Namespace using the private key of the creator
- if NomNomRegOptIn is true, `nomNomNameType` in `NamespaceProperties` must be defined, and the path should be found as a key in the `nameTypes` object.
- Only one NameType can be bound to NomNoms
- if OrgNomRegOptIn is true, `orgNomNameType` in `NamespaceProperties` must be defined, and the path should be found as a key in the `nameTypes` object.
- - Only one NameType can be bound to OrgNoms
- Update: Not valid
- Delete: Not valid
```typescript
type Namespace = {
// originDna?: string, // used as the id of the namespace if present
nomNomRegOptIn: boolean; // this is the global name scope for agents
orgNomRegOptIn: boolean; // this is the global name scope for organizations
ttl: number; // u8
bindingPublicKey: string;
};
```
### `NamespaceProperties`
#### validation
- Create
- Check the required fields exist in input
- `NamespaceProperties.displayName`
- `NamespaceProperties.dnaVersions`
- `NamespaceProperties.orgDetail`
- `orgDetail.name`
- `orgDetail.email`
- `orgDetail.country`
- `orgDetail.city`
- `orgDetail.province`
- `NamespaceProperties.nameTypes`
- `NamespaceProperties.maintainers`
- `NamespaceProperties.marketingInfo`
- `NamespaceProperties.ownershipProof`
- `1 =< NamespaceProperties.displayName >= 50`
- `1 =< NamespaceProperties.orgDetail.name >= 50`
- 1 =< `appName` => 50
- 0 =< `description` => 300
- Update
- check the signature that is in the request and verify it with the binding public key
- sign the input using the private key caller and check that it matches one of the public keys in `maintainers` or `bindingPublicKey`
- Check required fields exist in input
- **For updating `NamespaceProperties`**
- `1 =< NamespaceProperties.displayName >= 50`
- `1 =< NamespaceProperties.orgDetail.name >= 50`
- **For updating `MarketingInfo`**
- 1 =< `appName` => 50
- **For updating `Nametypes`**
- if previous version had agentNameType id, it must be the same. If not, the agentNameType path must be found in the `nameTypes` array.
- if previous version had orgNameType id, it must be the same. If not, the orgNameType path must be found in the `nameTypes` array.
- Delete: Not valid
```typescript
export type NamespaceProperties = {
namespace: string // temporary uuid of namespace which becomes ActionHash later
displayName: string;
dnaVersions: string[];
orgDetail: Organization;
nameTypes: {
[nameTypePath: string]: NameType // The key should be replaced with EntryHash later when ported to Holochain.
};
nomNomNameType?: string; // the path of the name type bound to nomnom registry. This will be replaced with the EntryHash when ported over to holochain
orgNomNameType?: string; // the path of the name type bound to orgnom registry. This will be replaced with the EntryHash when ported over to Holochain.
maintainers: string[]; // Agent public keys of maintainers. Set by the owner of namespace.
marketingInfo: MarketingInfo;
ownershipProof: string; // Option<Signature>. Signature of the owner of namespace.
};
export type Organization = {
name: string;
email: string;
country: string;
province: string;
city: string;
contactNo: string;
};
export type MarketingInfo = {
logo?: string; // base64 of logo
appName: string;
appStatus: AppStatus:
description?: string
};
export enum AppStatus {
Development,
Alpha,
Beta,
}
```
### `NameType`
#### Validation
- Create
- required fields must exist
- check the signature that is in the request and verify it with the binding public key
- sign the input using the private key caller and check that it matches one of the public keys in `maintainers` or `bindingPublicKey`
- Regex must conform to a ES6 format
- `1 =< displayName >= 50`
- `ttl` 0
- `purchasePrice` = 0 if `PrePurchasedNameAllotment` is not empty
- `purchasePrice` cannot be 0 and needs to be `purchasePrice` >= 100 (minimum $1) if `PrePurchasedNameAllotment` is empty
- `reserved_names` < x
- `path` must be unique
- for `PrePurchasedNameAllotment`
- namespace must be the same id of the namespace as the nameType is part of
- nametypePath should match the path of the nameType the PrePurchasedNameAllotment is part of
- **Add NameType**
- the uuid of the namespace must be the same as the uuid of the namespace it belongs in
- Update
- check the signature that is in the request and verify it with the binding public key
- sign the input using the private key caller and check that it matches one of the public keys in `maintainers` or `bindingPublicKey`
- the namespace id provided in the URL must match the namespace id defined in the previous version of the NameType
- `1 =< displayName >= 50`
- `ttl` < 0
- `purchasePrice` = 0 if `PrePurchasedNameAllotment` is not empty in previous version
- `purchasePrice` >= 100 (minimum $1) if previous version of the NameType price > >=100
- **for `PrePurchasedNameAllotment`**
- `namespace` must be the same id of the namespace as the nameType is part of
- `nametypePath` should match the `path` of the `nameType` the `PrePurchasedNameAllotment` is part of
```typescript
export type NameType = {
path: string; // immutable(permanent) name of the name type
namespace: string; // temporary uuid of the namespace
validNameStructureRegex: string;
reserved_names: string[], // immutable
displayName: string;
ttl: number; // u8
purchasePrice: number; // u32 (in USD)
PrePurchasedNameAllotments: PrePurchasedNameAllotment[]; // can only be added. Delete is not allowed.
};
export type PrePurchasedNameAllotment = {
namespace: string;
nametypePath: string,
qty: number // bundle of free names purchased
}
```