gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet-go] branch master updated: Namestore and identity service integr


From: gnunet
Subject: [gnunet-go] branch master updated: Namestore and identity service integrated.
Date: Fri, 11 Nov 2022 09:44:40 +0100

This is an automated email from the git hooks/post-receive script.

bernd-fix pushed a commit to branch master
in repository gnunet-go.

The following commit(s) were added to refs/heads/master by this push:
     new 30f8a14  Namestore and identity service integrated.
30f8a14 is described below

commit 30f8a148ff551129b3ccc8f52e5dda1ec2104ee3
Author: Bernd Fix <brf@hoi-polloi.org>
AuthorDate: Fri Nov 11 09:43:03 2022 +0100

    Namestore and identity service integrated.
---
 src/gnunet/build.sh                                |   3 +-
 src/gnunet/cmd/zonemaster-go/main.go               |   8 +-
 src/gnunet/config/config.go                        |   1 +
 src/gnunet/config/gnunet-config.json               |   1 +
 src/gnunet/crypto/gns.go                           |  26 +
 src/gnunet/enums/blocktype_string.go               |   7 +-
 src/gnunet/enums/dht.go                            |   4 -
 src/gnunet/enums/dht_block_type.go                 |   1 +
 src/gnunet/enums/error_codes.go                    |  33 ++
 src/gnunet/enums/errorcode_string.go               |  63 ++
 src/gnunet/enums/{generate.go => generate/main.go} |  35 +-
 src/gnunet/enums/generators.go                     |  57 ++
 src/gnunet/enums/gns.go                            |  42 +-
 src/gnunet/enums/gns_flags.go                      |  46 ++
 src/gnunet/enums/gns_type.go                       |   2 +
 src/gnunet/enums/gnstype_string.go                 |   6 +-
 src/gnunet/enums/gnunet-error-codes.rec            | 136 +++++
 src/gnunet/enums/gnunet-error-codes.tpl            |  12 +
 src/gnunet/enums/gnunet-gns-flags.rec              |  43 ++
 src/gnunet/enums/gnunet-gns-flags.tpl              |  38 ++
 src/gnunet/enums/gnunet-gns.rec                    |  19 +-
 src/gnunet/enums/gnunet-signature.rec              | 404 ++++++++++++-
 src/gnunet/enums/messages.go                       |  36 +-
 src/gnunet/enums/msgtype_string.go                 | 656 +++++++++++----------
 src/gnunet/enums/signature.go                      |  23 -
 src/gnunet/enums/signature_purpose.go              |   1 +
 src/gnunet/enums/sigpurpose_string.go              |   7 +-
 src/gnunet/go.mod                                  |   2 +-
 src/gnunet/message/factory.go                      |  26 +-
 src/gnunet/message/msg_identity.go                 | 172 +-----
 src/gnunet/message/msg_namestore.go                | 598 +++++++++++++++++--
 src/gnunet/service/dht/blocks/gns.go               |  57 +-
 src/gnunet/service/gns/block_handler.go            |   4 +-
 src/gnunet/service/gns/dns.go                      |   2 +-
 src/gnunet/service/gns/module.go                   |  13 +-
 src/gnunet/service/gns/rr/gns.go                   |   2 +-
 src/gnunet/service/store/store_zonemaster.go       | 296 ++++------
 src/gnunet/service/store/store_zonemaster.sql      |  29 +-
 src/gnunet/service/store/store_zonemaster_test.go  |   4 +-
 src/gnunet/service/zonemaster/gui.go               | 150 ++++-
 src/gnunet/service/zonemaster/gui.htpl             |  17 +-
 src/gnunet/service/zonemaster/gui_css.htpl         |  12 +
 src/gnunet/service/zonemaster/gui_edit.htpl        |   1 +
 src/gnunet/service/zonemaster/gui_plugin.go        |  68 +++
 src/gnunet/service/zonemaster/gui_rr.htpl          |  36 +-
 src/gnunet/service/zonemaster/records.go           |  43 +-
 src/gnunet/service/zonemaster/service.go           | 138 +----
 src/gnunet/service/zonemaster/service_identity.go  | 108 +++-
 src/gnunet/service/zonemaster/service_namestore.go | 264 ++++++++-
 src/gnunet/service/zonemaster/zonemaster.go        | 136 ++++-
 src/gnunet/sync_with_gana.sh                       |  33 ++
 51 files changed, 2864 insertions(+), 1057 deletions(-)

diff --git a/src/gnunet/build.sh b/src/gnunet/build.sh
index 03899f7..ce79ce1 100755
--- a/src/gnunet/build.sh
+++ b/src/gnunet/build.sh
@@ -4,4 +4,5 @@ if [ "$1" = "withgen" ]; then
     go generate ./...
     shift
 fi
-go install $* -gcflags "-N -l" ./...
+
+go install $* -trimpath -gcflags "-N -l" ./...
diff --git a/src/gnunet/cmd/zonemaster-go/main.go 
b/src/gnunet/cmd/zonemaster-go/main.go
index 6380db0..fdd3ae9 100644
--- a/src/gnunet/cmd/zonemaster-go/main.go
+++ b/src/gnunet/cmd/zonemaster-go/main.go
@@ -75,7 +75,7 @@ func main() {
 
        // start services under zonemaster umbrella
        ctx, cancel := context.WithCancel(context.Background())
-       srv := zonemaster.NewService(ctx, nil)
+       srv := zonemaster.NewService(ctx, nil, config.Cfg.ZoneMaster.PlugIns)
        go srv.Run(ctx)
 
        // start UDS listener if service is specified
@@ -83,7 +83,7 @@ func main() {
                sockHdlr := service.NewSocketHandler("zonemaster", srv)
                if err = sockHdlr.Start(ctx, 
config.Cfg.ZoneMaster.Service.Socket, config.Cfg.ZoneMaster.Service.Params); 
err != nil {
                        logger.Printf(logger.ERROR, "[zonemaster] Error: '%s'", 
err.Error())
-                       return
+                       _ = sockHdlr.Stop()
                }
        }
 
@@ -101,9 +101,9 @@ func main() {
                var rpc *service.JRPCServer
                if rpc, err = service.RunRPCServer(ctx, ep); err != nil {
                        logger.Printf(logger.ERROR, "[zonemaster] RPC failed to 
start: %s", err.Error())
-                       return
+               } else {
+                       srv.InitRPC(rpc)
                }
-               srv.InitRPC(rpc)
        }
        // handle OS signals
        sigCh := make(chan os.Signal, 5)
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go
index 05f8cc9..1381d11 100644
--- a/src/gnunet/config/config.go
+++ b/src/gnunet/config/config.go
@@ -101,6 +101,7 @@ type ZoneMasterConfig struct {
        Period  int               `json:"period"`  // cycle period
        Storage util.ParameterSet `json:"storage"` // persistence mechanism for 
zone data
        GUI     string            `json:"gui"`     // listen address for HTTP 
GUI
+       PlugIns []string          `json:"plugins"` // list of plugins to load
 }
 
 //----------------------------------------------------------------------
diff --git a/src/gnunet/config/gnunet-config.json 
b/src/gnunet/config/gnunet-config.json
index cd19450..3f530d3 100644
--- a/src/gnunet/config/gnunet-config.json
+++ b/src/gnunet/config/gnunet-config.json
@@ -90,6 +90,7 @@
             "file": "${VAR_LIB}/gns/zonemaster.sqlite3"
         },
         "gui": "127.0.0.1:8100",
+        "plugins": [],
         "service": {
             "socket": "${RT_USER}/gnunet-service-zonemaster-go.sock",
             "params": {
diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go
index a1c26f6..6021d98 100644
--- a/src/gnunet/crypto/gns.go
+++ b/src/gnunet/crypto/gns.go
@@ -281,6 +281,11 @@ func NullZonePrivate(ztype enums.GNSType) (*ZonePrivate, 
uint16) {
        return zp, uint16(len(zp.KeyData) + 4)
 }
 
+// IsNull returns true for a NULL key
+func (zk *ZonePrivate) IsNull() bool {
+       return util.IsAll(zk.KeyData, 0)
+}
+
 // Bytes returns the binary representation
 func (zp *ZonePrivate) Bytes() []byte {
        buf := new(bytes.Buffer)
@@ -373,6 +378,27 @@ func NewZoneKey(d []byte) (zk *ZoneKey, err error) {
        return
 }
 
+// NullZoneKey returns a "NULL" ZonePrivate for a given key
+func NullZoneKey(ztype enums.GNSType) (*ZoneKey, uint16) {
+       // get factory for given zone type
+       impl, ok := zoneImpl[ztype]
+       if !ok {
+               return nil, 0
+       }
+       kd := make([]byte, impl.PublicSize)
+       zp := &ZoneKey{
+               Type:    ztype, // need key type for length calculation
+               KeyData: kd,    // untyped key data (all 0)
+               impl:    nil,   // no implementation!
+       }
+       return zp, uint16(len(zp.KeyData) + 4)
+}
+
+// IsNull returns true for a NULL key
+func (zk *ZoneKey) IsNull() bool {
+       return util.IsAll(zk.KeyData, 0)
+}
+
 // Bytes returns the binary representation (can be used with 'init()')
 func (zk *ZoneKey) Bytes() []byte {
        buf := new(bytes.Buffer)
diff --git a/src/gnunet/enums/blocktype_string.go 
b/src/gnunet/enums/blocktype_string.go
index cc14d79..b81cd19 100644
--- a/src/gnunet/enums/blocktype_string.go
+++ b/src/gnunet/enums/blocktype_string.go
@@ -24,18 +24,19 @@ func _() {
        _ = x[BLOCK_TYPE_SET_TEST-24]
        _ = x[BLOCK_TYPE_CONSENSUS_ELEMENT-25]
        _ = x[BLOCK_TYPE_SETI_TEST-26]
+       _ = x[BLOCK_TYPE_SETU_TEST-27]
 }
 
 const (
        _BlockType_name_0 = 
"BLOCK_TYPE_ANYBLOCK_TYPE_FS_DBLOCKBLOCK_TYPE_FS_IBLOCK"
        _BlockType_name_1 = 
"BLOCK_TYPE_FS_ONDEMANDBLOCK_TYPE_DHT_HELLOBLOCK_TYPE_TESTBLOCK_TYPE_FS_UBLOCKBLOCK_TYPE_DNSBLOCK_TYPE_GNS_NAMERECORDBLOCK_TYPE_REVOCATIONBLOCK_TYPE_DHT_URL_HELLO"
-       _BlockType_name_2 = 
"BLOCK_TYPE_REGEXBLOCK_TYPE_REGEX_ACCEPTBLOCK_TYPE_SET_TESTBLOCK_TYPE_CONSENSUS_ELEMENTBLOCK_TYPE_SETI_TEST"
+       _BlockType_name_2 = 
"BLOCK_TYPE_REGEXBLOCK_TYPE_REGEX_ACCEPTBLOCK_TYPE_SET_TESTBLOCK_TYPE_CONSENSUS_ELEMENTBLOCK_TYPE_SETI_TESTBLOCK_TYPE_SETU_TEST"
 )
 
 var (
        _BlockType_index_0 = [...]uint8{0, 14, 34, 54}
        _BlockType_index_1 = [...]uint8{0, 22, 42, 57, 77, 91, 116, 137, 161}
-       _BlockType_index_2 = [...]uint8{0, 16, 39, 58, 86, 106}
+       _BlockType_index_2 = [...]uint8{0, 16, 39, 58, 86, 106, 126}
 )
 
 func (i BlockType) String() string {
@@ -45,7 +46,7 @@ func (i BlockType) String() string {
        case 6 <= i && i <= 13:
                i -= 6
                return 
_BlockType_name_1[_BlockType_index_1[i]:_BlockType_index_1[i+1]]
-       case 22 <= i && i <= 26:
+       case 22 <= i && i <= 27:
                i -= 22
                return 
_BlockType_name_2[_BlockType_index_2[i]:_BlockType_index_2[i+1]]
        default:
diff --git a/src/gnunet/enums/dht.go b/src/gnunet/enums/dht.go
index ac04244..0e760ab 100644
--- a/src/gnunet/enums/dht.go
+++ b/src/gnunet/enums/dht.go
@@ -29,7 +29,3 @@ const (
 
        DHT_RO_DISCOVERY = 32768 // Peer discovery
 )
-
-//go:generate go run generate.go gnunet-dht.rec gnunet-dht.tpl 
dht_block_type.go
-
-//go:generate stringer -type=BlockType dht_block_type.go
diff --git a/src/gnunet/enums/dht_block_type.go 
b/src/gnunet/enums/dht_block_type.go
index db2ffe9..9a03bf0 100644
--- a/src/gnunet/enums/dht_block_type.go
+++ b/src/gnunet/enums/dht_block_type.go
@@ -23,6 +23,7 @@ BLOCK_TYPE_REGEX_ACCEPT BlockType = 23 // Block to store a 
cadet regex accepting
 BLOCK_TYPE_SET_TEST BlockType = 24 // Block for testing set/consensus.  If 
first byte of the block is non-zero, the block is considered invalid.
 BLOCK_TYPE_CONSENSUS_ELEMENT BlockType = 25 // Block type for consensus 
elements. Contains either special marker elements or a nested block.
 BLOCK_TYPE_SETI_TEST BlockType = 26 // Block for testing set intersection.  If 
first byte of the block is non-zero, the block is considered invalid.
+BLOCK_TYPE_SETU_TEST BlockType = 27 // Block for testing set union.  If first 
byte of the block is non-zero, the block is considered invalid.
 
 )
 
diff --git a/src/gnunet/enums/error_codes.go b/src/gnunet/enums/error_codes.go
new file mode 100644
index 0000000..2c1caf7
--- /dev/null
+++ b/src/gnunet/enums/error_codes.go
@@ -0,0 +1,33 @@
+// Code generated by enum generator; DO NOT EDIT.
+
+//nolint:stylecheck // allow non-camel-case for constants
+package enums
+
+type ErrorCode int32
+
+// Error code values
+const (
+EC_NONE ErrorCode = 0 // No error (success).
+EC_UNKNOWN ErrorCode = 1 // Unknown and unspecified error.
+EC_SERVICE_COMMUNICATION_FAILED ErrorCode = 101 // Communication with service 
failed.
+EC_IDENTITY_NOT_FOUND ErrorCode = 200 // Ego not found.
+EC_IDENTITY_NAME_CONFLICT ErrorCode = 201 // Identifier already in use for 
another ego.
+EC_IDENTITY_INVALID ErrorCode = 202 // The given ego is invalid or malformed.
+EC_NAMESTORE_UNKNOWN ErrorCode = 5000 // Unknown namestore error.
+EC_NAMESTORE_ITERATION_FAILED ErrorCode = 5001 // Zone iteration failed.
+EC_NAMESTORE_ZONE_NOT_FOUND ErrorCode = 5002 // Zone not found.
+EC_NAMESTORE_RECORD_NOT_FOUND ErrorCode = 5003 // Record not found.
+EC_NAMESTORE_RECORD_DELETE_FAILED ErrorCode = 5004 // Zone iteration failed.
+EC_NAMESTORE_ZONE_EMPTY ErrorCode = 5005 // Zone does not contain any records.
+EC_NAMESTORE_LOOKUP_ERROR ErrorCode = 5006 // Failed to lookup record.
+EC_NAMESTORE_NO_RECORDS_GIVEN ErrorCode = 5007 // No records given.
+EC_NAMESTORE_RECORD_DATA_INVALID ErrorCode = 5008 // Record data invalid.
+EC_NAMESTORE_NO_LABEL_GIVEN ErrorCode = 5009 // No label given.
+EC_NAMESTORE_NO_RESULTS ErrorCode = 5010 // No results given.
+EC_NAMESTORE_RECORD_EXISTS ErrorCode = 5011 // Record already exists.
+EC_NAMESTORE_RECORD_TOO_BIG ErrorCode = 5012 // Record size exceeds maximum 
limit.
+EC_NAMESTORE_BACKEND_FAILED ErrorCode = 5013 // There was an error in the 
database backend.
+EC_NAMESTORE_STORE_FAILED ErrorCode = 5014 // Failed to store the given 
records.
+EC_NAMESTORE_LABEL_INVALID ErrorCode = 5015 // Label invalid or malformed.
+
+)
diff --git a/src/gnunet/enums/errorcode_string.go 
b/src/gnunet/enums/errorcode_string.go
new file mode 100644
index 0000000..6b00baa
--- /dev/null
+++ b/src/gnunet/enums/errorcode_string.go
@@ -0,0 +1,63 @@
+// Code generated by "stringer -type=ErrorCode error_codes.go"; DO NOT EDIT.
+
+package enums
+
+import "strconv"
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant 
values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[EC_NONE-0]
+       _ = x[EC_UNKNOWN-1]
+       _ = x[EC_SERVICE_COMMUNICATION_FAILED-101]
+       _ = x[EC_IDENTITY_NOT_FOUND-200]
+       _ = x[EC_IDENTITY_NAME_CONFLICT-201]
+       _ = x[EC_IDENTITY_INVALID-202]
+       _ = x[EC_NAMESTORE_UNKNOWN-5000]
+       _ = x[EC_NAMESTORE_ITERATION_FAILED-5001]
+       _ = x[EC_NAMESTORE_ZONE_NOT_FOUND-5002]
+       _ = x[EC_NAMESTORE_RECORD_NOT_FOUND-5003]
+       _ = x[EC_NAMESTORE_RECORD_DELETE_FAILED-5004]
+       _ = x[EC_NAMESTORE_ZONE_EMPTY-5005]
+       _ = x[EC_NAMESTORE_LOOKUP_ERROR-5006]
+       _ = x[EC_NAMESTORE_NO_RECORDS_GIVEN-5007]
+       _ = x[EC_NAMESTORE_RECORD_DATA_INVALID-5008]
+       _ = x[EC_NAMESTORE_NO_LABEL_GIVEN-5009]
+       _ = x[EC_NAMESTORE_NO_RESULTS-5010]
+       _ = x[EC_NAMESTORE_RECORD_EXISTS-5011]
+       _ = x[EC_NAMESTORE_RECORD_TOO_BIG-5012]
+       _ = x[EC_NAMESTORE_BACKEND_FAILED-5013]
+       _ = x[EC_NAMESTORE_STORE_FAILED-5014]
+       _ = x[EC_NAMESTORE_LABEL_INVALID-5015]
+}
+
+const (
+       _ErrorCode_name_0 = "EC_NONEEC_UNKNOWN"
+       _ErrorCode_name_1 = "EC_SERVICE_COMMUNICATION_FAILED"
+       _ErrorCode_name_2 = 
"EC_IDENTITY_NOT_FOUNDEC_IDENTITY_NAME_CONFLICTEC_IDENTITY_INVALID"
+       _ErrorCode_name_3 = 
"EC_NAMESTORE_UNKNOWNEC_NAMESTORE_ITERATION_FAILEDEC_NAMESTORE_ZONE_NOT_FOUNDEC_NAMESTORE_RECORD_NOT_FOUNDEC_NAMESTORE_RECORD_DELETE_FAILEDEC_NAMESTORE_ZONE_EMPTYEC_NAMESTORE_LOOKUP_ERROREC_NAMESTORE_NO_RECORDS_GIVENEC_NAMESTORE_RECORD_DATA_INVALIDEC_NAMESTORE_NO_LABEL_GIVENEC_NAMESTORE_NO_RESULTSEC_NAMESTORE_RECORD_EXISTSEC_NAMESTORE_RECORD_TOO_BIGEC_NAMESTORE_BACKEND_FAILEDEC_NAMESTORE_STORE_FAILEDEC_NAMESTORE_LABEL_INVALID"
+)
+
+var (
+       _ErrorCode_index_0 = [...]uint8{0, 7, 17}
+       _ErrorCode_index_2 = [...]uint8{0, 21, 46, 65}
+       _ErrorCode_index_3 = [...]uint16{0, 20, 49, 76, 105, 138, 161, 186, 
215, 247, 274, 297, 323, 350, 377, 402, 428}
+)
+
+func (i ErrorCode) String() string {
+       switch {
+       case 0 <= i && i <= 1:
+               return 
_ErrorCode_name_0[_ErrorCode_index_0[i]:_ErrorCode_index_0[i+1]]
+       case i == 101:
+               return _ErrorCode_name_1
+       case 200 <= i && i <= 202:
+               i -= 200
+               return 
_ErrorCode_name_2[_ErrorCode_index_2[i]:_ErrorCode_index_2[i+1]]
+       case 5000 <= i && i <= 5015:
+               i -= 5000
+               return 
_ErrorCode_name_3[_ErrorCode_index_3[i]:_ErrorCode_index_3[i+1]]
+       default:
+               return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")"
+       }
+}
diff --git a/src/gnunet/enums/generate.go b/src/gnunet/enums/generate/main.go
similarity index 78%
rename from src/gnunet/enums/generate.go
rename to src/gnunet/enums/generate/main.go
index 0e02850..aa60be3 100644
--- a/src/gnunet/enums/generate.go
+++ b/src/gnunet/enums/generate/main.go
@@ -33,15 +33,17 @@ import (
 
 // Record in the GANA registry (for a given type)
 type Record struct {
-       Number     string
-       Name       string
-       Comment    string
-       References string
+       Number      string
+       Name        string
+       Comment     string
+       Package     string
+       References  string
+       Description string
 }
 
 // String returns a readable record string
 func (rec *Record) String() string {
-       return fmt.Sprintf("[%s:%s]\n", rec.Number, rec.Name)
+       return fmt.Sprintf("[%s:%s]", rec.Number, rec.Name)
 }
 
 // go:generate generator to read recfiles and fill templates (not exactly
@@ -66,21 +68,24 @@ func main() {
                log.Fatal(err)
        }
        defer in.Close()
+       log.Println("-----------------------------------------------")
+       log.Printf("Processing %s\n", args[0])
+       log.Println("-----------------------------------------------")
 
        rdr := bufio.NewReader(in)
        state := 0
        var recs []*Record
        var rec *Record
-       for {
+       done := false
+       for !done {
                // read next line from recfile
                buf, _, err := rdr.ReadLine()
                if err != nil {
                        if err == io.EOF {
-                               break
+                               done = true
                        }
                }
                line := strings.TrimSpace(string(buf))
-               log.Printf("[%d] %s\n", state, line)
 
                // perform state machine:
                switch state {
@@ -98,9 +103,11 @@ func main() {
                // read record data
                case 1:
                        if len(line) == 0 {
-                               // record done; add to list
-                               log.Println("Record: " + rec.String())
-                               recs = append(recs, rec)
+                               // record done
+                               if rec.Package == "GNUnet" || rec.Package == "" 
{
+                                       log.Println("Record: " + rec.String())
+                                       recs = append(recs, rec)
+                               }
                                rec = nil
                                state = 0
                                continue
@@ -110,10 +117,16 @@ func main() {
                        switch kv[0] {
                        case "Number":
                                rec.Number = strings.TrimSpace(kv[1])
+                       case "Value":
+                               rec.Number = strings.TrimSpace(kv[1])
                        case "Name":
                                rec.Name = strings.TrimSpace(kv[1])
                        case "Comment":
                                rec.Comment = strings.TrimSpace(kv[1])
+                       case "Description":
+                               rec.Description = strings.TrimSpace(kv[1])
+                       case "Package":
+                               rec.Package = strings.TrimSpace(kv[1])
                        case "References":
                                rec.References = strings.TrimSpace(kv[1])
                        }
diff --git a/src/gnunet/enums/generators.go b/src/gnunet/enums/generators.go
new file mode 100644
index 0000000..4c11f55
--- /dev/null
+++ b/src/gnunet/enums/generators.go
@@ -0,0 +1,57 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019-2022 Bernd Fix  >Y<
+//
+// gnunet-go is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package enums
+
+//----------------------------------------------------------------------
+// Signature purposes
+//----------------------------------------------------------------------
+
+//go:generate go run generate/main.go gnunet-signature.rec 
gnunet-signature.tpl signature_purpose.go
+
+//go:generate stringer -type=SigPurpose signature_purpose.go
+
+//----------------------------------------------------------------------
+// Error codes
+//----------------------------------------------------------------------
+
+//go:generate go run generate/main.go gnunet-error-codes.rec 
gnunet-error-codes.tpl error_codes.go
+
+//go:generate stringer -type=ErrorCode error_codes.go
+
+//----------------------------------------------------------------------
+// DHT block types
+//----------------------------------------------------------------------
+
+//go:generate go run generate/main.go gnunet-dht.rec gnunet-dht.tpl 
dht_block_type.go
+
+//go:generate stringer -type=BlockType dht_block_type.go
+
+//----------------------------------------------------------------------
+// GNS record types
+//----------------------------------------------------------------------
+
+//go:generate go run generate/main.go gnunet-gns.rec gnunet-gns.tpl gns_type.go
+
+//go:generate stringer -type=GNSType gns_type.go
+
+//----------------------------------------------------------------------
+// GNS record flags
+//----------------------------------------------------------------------
+
+//go:generate go run generate/main.go gnunet-gns-flags.rec 
gnunet-gns-flags.tpl gns_flags.go
diff --git a/src/gnunet/enums/gns.go b/src/gnunet/enums/gns.go
index 8f786f8..8a98d2d 100644
--- a/src/gnunet/enums/gns.go
+++ b/src/gnunet/enums/gns.go
@@ -19,16 +19,34 @@
 //nolint:stylecheck // allow non-camel-case for constants
 package enums
 
-// GNSFlag type
-type GNSFlag uint32
+//----------------------------------------------------------------------
+// GNS filters
+//----------------------------------------------------------------------
+
+type GNSFilter uint16
 
 const (
-       // GNS record flags
-       GNS_FLAG_PRIVATE GNSFlag = 2  // Record is not shared on the DHT
-       GNS_FLAG_SUPPL   GNSFlag = 4  // Supplemental records (e.g. NICK) in a 
block
-       GNS_FLAG_EXPREL  GNSFlag = 8  // Expire time in record is in relative 
time.
-       GNS_FLAG_SHADOW  GNSFlag = 16 // Record is ignored if non-expired 
records of same type exist in block
+       // GNS filters
+       GNS_FILTER_NONE                GNSFilter = 0
+       GNS_FILTER_INCLUDE_MAINTENANCE GNSFilter = 1
+       GNS_FILTER_OMIT_PRIVATE        GNSFilter = 2
+)
+
+//----------------------------------------------------------------------
+// GNS type/flag combination (spec)
+//----------------------------------------------------------------------
+
+// GNSSpec is the combination of type and flags
+type GNSSpec struct {
+       Type  GNSType
+       Flags GNSFlag
+}
+
+//----------------------------------------------------------------------
+// Local settings
+//----------------------------------------------------------------------
 
+const (
        // GNS_LocalOptions
        GNS_LO_DEFAULT      = 0 // Defaults, look in cache, then in DHT.
        GNS_LO_NO_DHT       = 1 // Never look in the DHT, keep request to local 
cache.
@@ -38,13 +56,3 @@ const (
 
        GNS_REPLICATION_LEVEL = 10
 )
-
-//go:generate go run generate.go gnunet-gns.rec gnunet-gns.tpl gns_type.go
-
-//go:generate stringer -type=GNSType gns_type.go
-
-// GNSSpec is the combination of type and flags
-type GNSSpec struct {
-       Type  GNSType
-       Flags GNSFlag
-}
diff --git a/src/gnunet/enums/gns_flags.go b/src/gnunet/enums/gns_flags.go
new file mode 100644
index 0000000..e5458cb
--- /dev/null
+++ b/src/gnunet/enums/gns_flags.go
@@ -0,0 +1,46 @@
+// Code generated by enum generator; DO NOT EDIT.
+
+//nolint:stylecheck // allow non-camel-case for constants
+package enums
+
+//----------------------------------------------------------------------
+// Resource Record Flags
+//----------------------------------------------------------------------
+
+// GNSFlag type
+type GNSFlag uint16
+
+const (
+       // GNS record flags
+
+       GNS_FLAG_CRITICAL GNSFlag = (1<<(15-15)) // This record is critical. If 
it cannot be processed (for example because the record type is unknown) 
resolution MUST fail
+
+       GNS_FLAG_SHADOW GNSFlag = (1<<(15-14)) // This record should not be 
used unless all (other) records in the set with an absolute expiration time 
have expired.
+
+       GNS_FLAG_SUPPLEMENTAL GNSFlag = (1<<(15-13)) // This is a supplemental 
record.
+
+       GNS_FLAG_RELATIVE_EXPIRATION GNSFlag = (1<<(15-1)) // This expiration 
time of the record is a relative time (not an absolute time). Used in GNUnet 
implementation.
+
+       GNS_FLAG_PRIVATE GNSFlag = (1<<(15-0)) // This is a private record of 
this peer and it should thus not be published.
+
+)
+
+// List flags as strings
+func (gf GNSFlag) List() (flags []string) {
+       if gf&GNS_FLAG_PRIVATE != 0 {
+               flags = append(flags, "Private")
+       }
+       if gf&GNS_FLAG_SHADOW != 0 {
+               flags = append(flags, "Shadow")
+       }
+       if gf&GNS_FLAG_SUPPLEMENTAL != 0 {
+               flags = append(flags, "Suppl")
+       }
+       if gf&GNS_FLAG_CRITICAL != 0 {
+               flags = append(flags, "Critical")
+       }
+       if gf&GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+               flags = append(flags, "TTL")
+       }
+       return
+}
diff --git a/src/gnunet/enums/gns_type.go b/src/gnunet/enums/gns_type.go
index d4cf6a6..1b10c89 100644
--- a/src/gnunet/enums/gns_type.go
+++ b/src/gnunet/enums/gns_type.go
@@ -69,5 +69,7 @@ GNS_TYPE_EDKEY GNSType = 65556 // Record type for EDKEY zone 
delegations.
 GNS_TYPE_ERIS_READ_CAPABILITY GNSType = 65557 // Encoding for Robust Immutable 
Storage (ERIS) binary read capability
 GNS_TYPE_MESSENGER_ROOM_ENTRY GNSType = 65558 // Record type to share an entry 
of a messenger room
 GNS_TYPE_TOMBSTONE GNSType = 65559 // Record type to indicate a previously 
delete record (PRIVATE only)
+GNS_TYPE_MESSENGER_ROOM_DETAILS GNSType = 65560 // Record type to store 
details about a messenger room
+GNS_TYPE_DID_DOCUMENT GNSType = 65561 // Record type to store DID Documents
 
 )
diff --git a/src/gnunet/enums/gnstype_string.go 
b/src/gnunet/enums/gnstype_string.go
index 1a55f01..d0d55e7 100644
--- a/src/gnunet/enums/gnstype_string.go
+++ b/src/gnunet/enums/gnstype_string.go
@@ -69,9 +69,11 @@ func _() {
        _ = x[GNS_TYPE_ERIS_READ_CAPABILITY-65557]
        _ = x[GNS_TYPE_MESSENGER_ROOM_ENTRY-65558]
        _ = x[GNS_TYPE_TOMBSTONE-65559]
+       _ = x[GNS_TYPE_MESSENGER_ROOM_DETAILS-65560]
+       _ = x[GNS_TYPE_DID_DOCUMENT-65561]
 }
 
-const _GNSType_name = 
"GNS_TYPE_ANYGNS_TYPE_DNS_AGNS_TYPE_DNS_NSGNS_TYPE_DNS_CNAMEGNS_TYPE_DNS_SOAGNS_TYPE_DNS_PTRGNS_TYPE_DNS_MXGNS_TYPE_DNS_TXTGNS_TYPE_DNS_RPGNS_TYPE_DNS_AFSDBGNS_TYPE_DNS_SIGGNS_TYPE_DNS_KEYGNS_TYPE_DNS_AAAAGNS_TYPE_DNS_LOCGNS_TYPE_DNS_SRVGNS_TYPE_DNS_NAPTRGNS_TYPE_DNS_KXGNS_TYPE_DNS_CERTGNS_TYPE_DNS_DNAMEGNS_TYPE_DNS_APLGNS_TYPE_DNS_DSGNS_TYPE_DNS_SSHFPGNS_TYPE_DNS_IPSECKEYGNS_TYPE_DNS_RRSIGGNS_TYPE_DNS_NSECGNS_TYPE_DNS_DNSKEYGNS_TYPE_DNS_DHCIDGNS_TYPE_DNS_NSEC3GNS_T
 [...]
+const _GNSType_name = 
"GNS_TYPE_ANYGNS_TYPE_DNS_AGNS_TYPE_DNS_NSGNS_TYPE_DNS_CNAMEGNS_TYPE_DNS_SOAGNS_TYPE_DNS_PTRGNS_TYPE_DNS_MXGNS_TYPE_DNS_TXTGNS_TYPE_DNS_RPGNS_TYPE_DNS_AFSDBGNS_TYPE_DNS_SIGGNS_TYPE_DNS_KEYGNS_TYPE_DNS_AAAAGNS_TYPE_DNS_LOCGNS_TYPE_DNS_SRVGNS_TYPE_DNS_NAPTRGNS_TYPE_DNS_KXGNS_TYPE_DNS_CERTGNS_TYPE_DNS_DNAMEGNS_TYPE_DNS_APLGNS_TYPE_DNS_DSGNS_TYPE_DNS_SSHFPGNS_TYPE_DNS_IPSECKEYGNS_TYPE_DNS_RRSIGGNS_TYPE_DNS_NSECGNS_TYPE_DNS_DNSKEYGNS_TYPE_DNS_DHCIDGNS_TYPE_DNS_NSEC3GNS_T
 [...]
 
 var _GNSType_map = map[GNSType]string{
        0:     _GNSType_name[0:12],
@@ -135,6 +137,8 @@ var _GNSType_map = map[GNSType]string{
        65557: _GNSType_name[1019:1048],
        65558: _GNSType_name[1048:1077],
        65559: _GNSType_name[1077:1095],
+       65560: _GNSType_name[1095:1126],
+       65561: _GNSType_name[1126:1147],
 }
 
 func (i GNSType) String() string {
diff --git a/src/gnunet/enums/gnunet-error-codes.rec 
b/src/gnunet/enums/gnunet-error-codes.rec
new file mode 100644
index 0000000..e833bb5
--- /dev/null
+++ b/src/gnunet/enums/gnunet-error-codes.rec
@@ -0,0 +1,136 @@
+# -*- mode: rec -*-
+#
+# Registry for GNUnet errors.
+#
+%rec: GnunetErrorCode
+%key: Value
+%typedef: ValueRange_t range 0 9999
+%type: Value ValueRange_t
+%mandatory: Value
+%typedef: Description_t regexp 
|^[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_\(\)\.,;!"':#\/
 -]*$|
+%type: Description Description_t
+%mandatory: Description
+%typedef: Name_t regexp 
/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ_][ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789]*$/
+%type: Name Name_t
+%unique: Name
+%mandatory: Name
+# A status of 0 means no HTTP status is associated
+%type: HttpStatus rec HttpStatusCode
+%mandatory: HttpStatus
+%sort: Value
+
+Value: 0
+Name: NONE
+Description: No error (success).
+HttpStatus: 0
+
+Value: 1
+Name: UNKNOWN
+Description: Unknown and unspecified error.
+HttpStatus: 500
+
+Value: 101
+Name: SERVICE_COMMUNICATION_FAILED
+Description: Communication with service failed.
+HttpStatus: 500
+
+# IDENTITY Error Codes
+
+Value: 200
+Name: IDENTITY_NOT_FOUND
+Description: Ego not found.
+HttpStatus: 404
+
+Value: 201
+Name: IDENTITY_NAME_CONFLICT
+Description: Identifier already in use for another ego.
+HttpStatus: 409
+
+Value: 202
+Name: IDENTITY_INVALID
+Description: The given ego is invalid or malformed.
+HttpStatus: 500
+
+# NAMESTORE Error Codes
+
+Value: 5000
+Name: NAMESTORE_UNKNOWN
+Description: Unknown namestore error.
+HttpStatus: 500
+
+Value: 5001
+Name: NAMESTORE_ITERATION_FAILED
+Description: Zone iteration failed.
+HttpStatus: 500
+
+Value: 5002
+Name: NAMESTORE_ZONE_NOT_FOUND
+Description: Zone not found.
+HttpStatus: 404
+
+Value: 5003
+Name: NAMESTORE_RECORD_NOT_FOUND
+Description: Record not found.
+HttpStatus: 404
+
+Value: 5004
+Name: NAMESTORE_RECORD_DELETE_FAILED
+Description: Zone iteration failed.
+HttpStatus: 500
+
+Value: 5005
+Name: NAMESTORE_ZONE_EMPTY
+Description: Zone does not contain any records.
+HttpStatus: 404
+
+Value: 5006
+Name: NAMESTORE_LOOKUP_ERROR
+Description: Failed to lookup record.
+HttpStatus: 500
+
+Value: 5007
+Name: NAMESTORE_NO_RECORDS_GIVEN
+Description: No records given.
+HttpStatus: 400
+
+Value: 5008
+Name: NAMESTORE_RECORD_DATA_INVALID
+Description: Record data invalid.
+HttpStatus: 400
+
+Value: 5009
+Name: NAMESTORE_NO_LABEL_GIVEN
+Description: No label given.
+HttpStatus: 400
+
+Value: 5010
+Name: NAMESTORE_NO_RESULTS
+Description: No results given.
+HttpStatus: 404
+
+Value: 5011
+Name: NAMESTORE_RECORD_EXISTS
+Description: Record already exists.
+HttpStatus: 409
+
+Value: 5012
+Name: NAMESTORE_RECORD_TOO_BIG
+Description: Record size exceeds maximum limit.
+HttpStatus: 500
+
+Value: 5013
+Name: NAMESTORE_BACKEND_FAILED
+Description: There was an error in the database backend.
+HttpStatus: 500
+
+Value: 5014
+Name: NAMESTORE_STORE_FAILED
+Description: Failed to store the given records.
+HttpStatus: 500
+
+Value: 5015
+Name: NAMESTORE_LABEL_INVALID
+Description: Label invalid or malformed.
+HttpStatus: 400
+
+
diff --git a/src/gnunet/enums/gnunet-error-codes.tpl 
b/src/gnunet/enums/gnunet-error-codes.tpl
new file mode 100644
index 0000000..44d1d1e
--- /dev/null
+++ b/src/gnunet/enums/gnunet-error-codes.tpl
@@ -0,0 +1,12 @@
+// Code generated by enum generator; DO NOT EDIT.
+
+//nolint:stylecheck // allow non-camel-case for constants
+package enums
+
+type ErrorCode int32
+
+// Error code values
+const (
+{{ range $i, $kv := . }}EC_{{.Name}} ErrorCode = {{.Number}} // 
{{.Description}}
+{{ end }}
+)
diff --git a/src/gnunet/enums/gnunet-gns-flags.rec 
b/src/gnunet/enums/gnunet-gns-flags.rec
new file mode 100644
index 0000000..22aa750
--- /dev/null
+++ b/src/gnunet/enums/gnunet-gns-flags.rec
@@ -0,0 +1,43 @@
+# -*- mode: rec -*-
+#
+# Registry for GNU Name System record flags
+#
+
+%rec: RecordType
+%key: Number
+%typedef: FlagRange_t range 0 15
+%type: Number FlagRange_t
+%mandatory: Number
+%typedef: Name_t regexp 
/^[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-]*$/
+%type: Name Name_t
+%unique: Name
+%mandatory: Name
+%mandatory: Comment
+%allowed: Contact
+%allowed: References
+%sort: Number Name Contact References
+
+Number: 15
+Name: CRITICAL
+Comment: This record is critical. If it cannot be processed (for example 
because the record type is unknown) resolution MUST fail
+References: https://lsd.gnunet.org/lsd0001
+
+Number: 14
+Name: SHADOW
+Comment: This record should not be used unless all (other) records in the set 
with an absolute expiration time have expired.
+References: https://lsd.gnunet.org/lsd0001
+
+Number: 13
+Name: SUPPLEMENTAL
+Comment: This is a supplemental record.
+References: https://lsd.gnunet.org/lsd0001
+
+Number: 1
+Name: RELATIVE_EXPIRATION
+Comment: This expiration time of the record is a relative time (not an 
absolute time). Used in GNUnet implementation.
+References: https://git.gnunet.org/gnunet.git
+
+Number: 0
+Name: PRIVATE
+Comment: This is a private record of this peer and it should thus not be 
published.
+References: https://git.gnunet.org/gnunet.git
diff --git a/src/gnunet/enums/gnunet-gns-flags.tpl 
b/src/gnunet/enums/gnunet-gns-flags.tpl
new file mode 100644
index 0000000..8b434b2
--- /dev/null
+++ b/src/gnunet/enums/gnunet-gns-flags.tpl
@@ -0,0 +1,38 @@
+// Code generated by enum generator; DO NOT EDIT.
+
+//nolint:stylecheck // allow non-camel-case for constants
+package enums
+
+//----------------------------------------------------------------------
+// Resource Record Flags
+//----------------------------------------------------------------------
+
+// GNSFlag type
+type GNSFlag uint16
+
+const (
+       // GNS record flags
+{{ range $i, $kv := . }}
+       GNS_FLAG_{{.Name}} GNSFlag = (1<<(15-{{.Number}})) // {{.Comment}}
+{{ end }}
+)
+
+// List flags as strings
+func (gf GNSFlag) List() (flags []string) {
+       if gf&GNS_FLAG_PRIVATE != 0 {
+               flags = append(flags, "Private")
+       }
+       if gf&GNS_FLAG_SHADOW != 0 {
+               flags = append(flags, "Shadow")
+       }
+       if gf&GNS_FLAG_SUPPLEMENTAL != 0 {
+               flags = append(flags, "Suppl")
+       }
+       if gf&GNS_FLAG_CRITICAL != 0 {
+               flags = append(flags, "Critical")
+       }
+       if gf&GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+               flags = append(flags, "TTL")
+       }
+       return
+}
diff --git a/src/gnunet/enums/gnunet-gns.rec b/src/gnunet/enums/gnunet-gns.rec
index 8fc0c77..dd91cd5 100644
--- a/src/gnunet/enums/gnunet-gns.rec
+++ b/src/gnunet/enums/gnunet-gns.rec
@@ -19,32 +19,32 @@
 Number: 65536
 Name: PKEY
 Comment: GNS zone transfer
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65537
 Name: NICK
 Comment: GNS nick names
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65538
 Name: LEHO
 Comment: legacy hostnames
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65539
 Name: VPN
 Comment: VPN resolution
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65540
 Name: GNS2DNS
 Comment: Delegation to DNS
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65541
 Name: BOX
 Comment: Boxed records (see TLSA/SRV handling in GNS)
-References: LSD0001
+References: https://lsd.gnunet.org/lsd0001
 
 Number: 65542
 Name: PLACE
@@ -84,7 +84,7 @@ Contact: schanzen@gnunet.org
 Number: 65551
 Name: REDIRECT
 Comment: Resolver redirects
-Contact: LSD0001
+Contact: https://lsd.gnunet.org/lsd0001
 
 Number: 65552
 Name: RECLAIM_OIDC_CLIENT
@@ -130,3 +130,8 @@ Number: 65560
 Name: MESSENGER_ROOM_DETAILS
 Comment: Record type to store details about a messenger room
 Contact: thejackimonster@gmail.com
+
+Number: 65561
+Name: DID_DOCUMENT
+Comment: Record type to store DID Documents
+Contact: schanzen@gnunet.org
diff --git a/src/gnunet/enums/gnunet-signature.rec 
b/src/gnunet/enums/gnunet-signature.rec
index 79f9796..fbd57e4 100644
--- a/src/gnunet/enums/gnunet-signature.rec
+++ b/src/gnunet/enums/gnunet-signature.rec
@@ -1,6 +1,6 @@
 # -*- mode: rec -*-
 #
-# Registry for GNUnet Signature purposes
+# Registry for Signature purposes
 #
 
 %rec: SignaturePurpose
@@ -12,172 +12,572 @@
 %unique: Name
 %mandatory: Name
 %mandatory: Comment
+%mandatory: Package
 %allowed: Subsystem
 %sort: Number Name
 
 
+#-----------------------------------------------------------------------------
+# GNUnet
+
 Number: 0
 Name: TEST
 Comment: Test signature, not valid for anything other than writing a test. 
(Note that the signature verification code will accept this value).
+Package: GNUnet
 Subsystem: GNUnet
 
 Number: 1
 Name: TRANSPORT_PONG_OWN
 Comment: Signature for confirming that this peer uses a particular address.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 2
 Name: TRANSPORT_DISCONNECT
 Comment: Signature for confirming that this peer intends to disconnect.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 3
 Name: REVOCATION
 Comment: Signature for confirming a key revocation.
+Package: GNUnet
 Subsystem: GNUnet-Revocation
 
 Number: 4
 Name: NAMESPACE_ADVERTISEMENT
 Comment: Signature for a namespace/pseudonym advertisement (by the namespace 
owner).
+Package: GNUnet
 Subsystem: GNUnet-FS
 
 Number: 5
 Name: PEER_PLACEMENT
 Comment: Signature by which a peer affirms that it is providing a certain bit 
of content for use in LOCation URIs.
+Package: GNUnet
 Subsystem: GNUnet-FS
 
 Number: 6
 Name: DHT_HOP
 Comment: Signature by which a peer affirms that it forwarded a message in the 
DHT.
+Package: GNUnet
 Subsystem: GNUnet-DHT
 
 Number: 7
 Name: HELLO
 Comment: Signature by which a peer affirms its address.
+Package: GNUnet
 Subsystem: GNUnet-HELLO
 
-
 Number: 11
 Name: DNS_RECORD
 Comment: Signature on a GNUNET_DNS_Advertisement.
+Package: GNUnet
 Subsystem: GNUnet-DNS+Exit
 
 Number: 12
 Name: CHAT_MESSAGE
 Comment: Signature of a chat message.
+Package: GNUnet
 Subsystem: GNUnet-MESSENGER
 
 Number: 13
 Name: CHAT_RECEIPT
 Comment: Signature of confirmation receipt for a chat message.
+Package: GNUnet
 Subsystem: GNUnet-MESSENGER
 
 Number: 14
 Name: NSE_SEND
 Comment: Signature of a network size estimate message.
+Package: GNUnet
 Subsystem: GNUnet-NSE
 
 Number: 15
 Name: GNS_RECORD_SIGN
 Comment: Signature of a gnunet naming system record block
+Package: GNUnet
 Subsystem: GNUnet-GNSRECORD
 
 Number: 16
 Name: SET_ECC_KEY
 Comment: Purpose is to set a session key.
+Package: GNUnet
 Subsystem: GNUnet-CORE
 
 Number: 17
 Name: FS_UBLOCK
 Comment: UBlock Signature, done using DSS, not ECC
+Package: GNUnet
 Subsystem: GNUnet-FS
 
 Number: 18
 Name: REGEX_ACCEPT
 Comment: Accept state in regex DFA.  Peer affirms that it offers the matching 
service.
+Package: GNUnet
 Subsystem: GNUnet-REGEX
 
 Number: 20
 Name: CONVERSATION_RING
 Comment: Signature of a conversation ring.
+Package: GNUnet
 Subsystem: GNUnet-CONVERSATION
 
 Number: 21
 Name: SECRETSHARING_DKG1
 Comment: Signature for the first round of distributed key generation.
+Package: GNUnet
 Subsystem: GNUnet-SECRETSHARING
 
 Number: 22
 Name: SECRETSHARING_DKG2
 Comment: Signature for the second round of distributed key generation.
+Package: GNUnet
 Subsystem: GNUnet-SECRETSHARING
 
 Number: 23
 Name: SECRETSHARING_DECRYPTION
 Comment: Signature for the cooperative decryption.
+Package: GNUnet
 Subsystem: GNUnet-SECRETSHARING
 
 Number: 27
 Name: RECLAIM_CODE_SIGN
 Comment: Signature for a GNUid Ticket
+Package: GNUnet
 Subsystem: Reclaim
 
 Number: 28
 Name: DELEGATE
 Comment: Signature for a GNUnet credential
+Package: GNUnet
 Subsystem: Reclaim
 
 Number: 29
 Name: TRANSPORT_ADDRESS
 Comment: Signature by a peer affirming that this is one of its addresses for 
the given time period.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 30
 Name: TRANSPORT_EPHEMERAL
 Comment: Signature by a peer affirming that the given ephemeral key is 
currently in use by that peer's transport service.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 31
 Name: COMMUNICATOR_TCP_HANDSHAKE
 Comment: Signature used by TCP communicator handshake.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT-TCP
 
 Number: 32
 Name: COMMUNICATOR_TCP_REKEY
 Comment: Signature used by TCP communicator rekey.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT-TCP
 
 Number: 33
 Name: COMMUNICATOR_UDP_HANDSHAKE
 Comment: Signature used by UDP communicator handshake.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT-UDP
 
 Number: 34
 Name: COMMUNICATOR_UDP_BROADCAST
 Comment: Signature used by UDP broadcasts.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT-UDP
 
 Number: 35
 Name: TRANSPORT_CHALLENGE
 Comment: Signature by a peer affirming that it received a challenge (and 
stating how long it expects the address on which the challenge was received to 
remain valid).
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 36
 Name: TRANSPORT_DV_HOP
 Comment: Signature by a peer affirming that it is on a DV path.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 37
 Name: TRANSPORT_DV_INITIATOR
 Comment: Signature by a peer affirming that it originated the DV path.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT
 
 Number: 38
 Name: CADET_CONNECTION_INITIATOR
 Comment: Signature by a peer that like to create a connection.
+Package: GNUnet
 Subsystem: GNUnet-CADET
 
 Number: 39
 Name: COMMUNICATOR_TCP_HANDSHAKE_ACK
 Comment: Signature by a peer sending back the nonce received at initial 
handshake.
+Package: GNUnet
 Subsystem: GNUnet-TRANSPORT-TCP
+
+#-----------------------------------------------------------------------------
+# GNU Taler, >= 1000
+
+#
+# Exchange offline signatures (with master key)
+#
+Number: 1018
+Name: MASTER_DRAIN_PROFIT
+Comment: Affirm wiring of exchange profits to operator account.
+Package: GNU Taler
+
+Number: 1019
+Name: MASTER_PARTNER_DETAILS
+Comment: Signature affirming a partner configuration for wads.
+Package: GNU Taler
+
+Number: 1020
+Name: MASTER_SIGNING_KEY_REVOKED
+Comment: The given revocation key was revoked and must no longer be used.
+Package: GNU Taler
+
+Number: 1021
+Name: MASTER_ADD_WIRE
+Comment: Add payto URI to the list of our wire methods.
+Package: GNU Taler
+
+Number: 1022
+Name: MASTER_GLOBAL_FEES
+Comment: Signature over global set of fees charged by the exchange.
+Package: GNU Taler
+
+Number: 1023
+Name: MASTER_DEL_WIRE
+Comment: Remove payto URI from the list of our wire methods.
+Package: GNU Taler
+
+Number: 1024
+Name: MASTER_SIGNING_KEY_VALIDITY
+Comment: Purpose for signing public keys signed by the exchange master key.
+Package: GNU Taler
+
+Number: 1025
+Name: MASTER_DENOMINATION_KEY_VALIDITY
+Comment: Purpose for denomination keys signed by the exchange master key.
+Package: GNU Taler
+
+Number: 1026
+Name: MASTER_ADD_AUDITOR
+Comment: Add an auditor to the list of our auditors.
+Package: GNU Taler
+
+Number: 1027
+Name: MASTER_DEL_AUDITOR
+Comment: Remove an auditor from the list of our auditors.
+Package: GNU Taler
+
+Number: 1028
+Name: MASTER_WIRE_FEES
+Comment: Fees charged per (aggregate) wire transfer to the merchant.
+Package: GNU Taler
+
+Number: 1029
+Name: MASTER_DENOMINATION_KEY_REVOKED
+Comment: The given revocation key was revoked and must no longer be used.
+Package: GNU Taler
+
+Number: 1030
+Name: MASTER_WIRE_DETAILS
+Comment: Signature where the Exchange confirms its IBAN details in the /wire 
response.
+Package: GNU Taler
+
+Number: 1031
+Name: MASTER_EXTENSION
+Comment: Set the configuration of an extension (age-restriction or peer2peer)
+Package: GNU Taler
+
+
+#
+# Exchange online signatures (with signing key)
+#
+
+Number: 1032
+Name: EXCHANGE_RESERVE_STATUS
+Comment: Purpose for the state of a reserve, signed by the exchange's signing 
key.
+Package: GNU Taler
+
+Number: 1033
+Name: EXCHANGE_CONFIRM_DEPOSIT
+Comment: Signature where the Exchange confirms a deposit request.
+Package: GNU Taler
+
+Number: 1034
+Name: EXCHANGE_CONFIRM_MELT
+Comment: Signature where the exchange (current signing key) confirms the 
no-reveal index for cut-and-choose and the validity of the melted coins.
+Package: GNU Taler
+
+Number: 1035
+Name: EXCHANGE_KEY_SET
+Comment: Signature where the Exchange confirms the full /keys response set.
+Package: GNU Taler
+
+Number: 1036
+Name: EXCHANGE_CONFIRM_WIRE
+Comment: Signature where the Exchange confirms the /track/transaction response.
+Package: GNU Taler
+
+Number: 1037
+Name: EXCHANGE_CONFIRM_WIRE_DEPOSIT
+Comment: Signature where the Exchange confirms the /wire/deposit response.
+Package: GNU Taler
+
+Number: 1038
+Name: EXCHANGE_CONFIRM_REFUND
+Comment: Signature where the Exchange confirms a refund request.
+Package: GNU Taler
+
+Number: 1039
+Name: EXCHANGE_CONFIRM_RECOUP
+Comment: Signature where the Exchange confirms a recoup.
+Package: GNU Taler
+
+Number: 1040
+Name: EXCHANGE_RESERVE_CLOSED
+Comment: Signature where the Exchange confirms it closed a reserve.
+Package: GNU Taler
+
+Number: 1041
+Name: EXCHANGE_CONFIRM_RECOUP_REFRESH
+Comment: Signature where the Exchange confirms a recoup-refresh operation.
+Package: GNU Taler
+
+Number: 1042
+Name: EXCHANGE_AFFIRM_DENOM_UNKNOWN
+Comment: Signature where the Exchange confirms that it does not know a 
denomination (hash).
+Package: GNU Taler
+
+Number: 1043
+Name: EXCHANGE_AFFIRM_DENOM_EXPIRED
+Comment: Signature where the Exchange confirms that it does not consider a 
denomination valid for the given operation at this time.
+Package: GNU Taler
+
+Number: 1044
+Name: EXCHANGE_ACCOUNT_SETUP_SUCCESS
+Comment: Signature by which an exchange affirms that an account successfully 
passed the KYC checks.
+Package: GNU Taler
+
+Number: 1045
+Name: EXCHANGE_CONFIRM_PURSE_CREATION
+Comment: Signature by which the exchange affirms that a purse was created with 
a certain amount deposited into it.
+Package: GNU Taler
+
+Number: 1046
+Name: EXCHANGE_CONFIRM_PURSE_MERGED
+Comment: Signature by which the exchange affirms that a purse was merged into 
a reserve with a certain amount in it.
+Package: GNU Taler
+
+Number: 1047
+Name: EXCHANGE_PURSE_STATUS
+Comment: Purpose for the state of a purse, signed by the exchange's signing 
key.
+Package: GNU Taler
+
+Number: 1048
+Name: EXCHANGE_RESERVE_ATTEST_DETAILS
+Comment: Signature by which the exchange attests identity attributes of a 
particular reserve owner.
+Package: GNU Taler
+
+Number: 1049
+Name: EXCHANGE_CONFIRM_PURSE_REFUND
+Comment: Signature by which the exchange confirms that a purse expired and a 
coin was refunded.
+Package: GNU Taler
+
+#
+# Auditor signatures
+#
+
+Number: 1064
+Name: AUDITOR_EXCHANGE_KEYS
+Comment: Signature where the auditor confirms that he is aware of certain 
denomination keys from the exchange.
+Package: GNU Taler
+
+#
+# Merchant signatures
+#
+
+Number: 1101
+Name: MERCHANT_CONTRACT
+Comment: Signature where the merchant confirms a contract (to the customer).
+Package: GNU Taler
+
+Number: 1102
+Name: MERCHANT_REFUND
+Comment: Signature where the merchant confirms a refund (of a coin).
+Package: GNU Taler
+
+Number: 1103
+Name: MERCHANT_TRACK_TRANSACTION
+Comment: Signature where the merchant confirms that he needs the wire transfer 
identifier for a deposit operation.
+Package: GNU Taler
+
+Number: 1104
+Name: MERCHANT_PAYMENT_OK
+Comment: Signature where the merchant confirms that the payment was successful
+Package: GNU Taler
+
+Number: 1107
+Name: MERCHANT_WIRE_DETAILS
+Comment: Signature where the merchant confirms its own (salted) wire details 
(not yet really used).
+Package: GNU Taler
+
+#
+# Wallet signatures
+#
+
+Number: 1200
+Name: WALLET_RESERVE_WITHDRAW
+Comment: Signature where the reserve key confirms a withdraw request. Signed 
with the reserve private key.
+Package: GNU Taler
+
+Number: 1201
+Name: WALLET_COIN_DEPOSIT
+Comment: Signature made by the wallet of a user to confirm a deposit of a coin.
+Package: GNU Taler
+
+Number: 1202
+Name: WALLET_COIN_MELT
+Comment: Signature using a coin key confirming the melting of a coin. Signed 
with the coin's private key.
+Package: GNU Taler
+
+Number: 1203
+Name: WALLET_COIN_RECOUP
+Comment: Signature using a coin key requesting recoup. Signed with the coin's 
private key.
+Package: GNU Taler
+
+Number: 1204
+Name: WALLET_COIN_LINK
+Comment: Signature using a coin key authenticating link data. Signed with the 
old coin's private key.
+Package: GNU Taler
+
+Number: 1205
+Name: WALLET_ACCOUNT_SETUP
+Comment: Signature using a reserve key by which a wallet requests a payment 
target UUID for itself. Signs over just a purpose (no body), as the signature 
only serves to demonstrate that the request comes from the wallet controlling 
the private key, and not some third party.
+Package: GNU Taler
+
+Number: 1206
+Name: WALLET_COIN_RECOUP_REFRESH
+Comment: Signature using a coin key requesting recoup-refresh. Signed with the 
coin private key.
+Package: GNU Taler
+
+Number: 1207
+Name: WALLET_AGE_ATTESTATION
+Comment: Signature using a age restriction key for attestation of a particular 
age/age-group.
+Package: GNU Taler
+
+Number: 1208
+Name: WALLET_RESERVE_HISTORY
+Comment: Request full reserve history and pay for it. Signed with the reserve 
private key.
+Package: GNU Taler
+
+Number: 1209
+Name: WALLET_RESERVE_STATUS
+Comment: Request detailed account status (for free). Signed with the reserve 
private key.
+Package: GNU Taler
+
+Number: 1210
+Name: WALLET_PURSE_CREATE
+Comment: Request purse creation (without reserve). Signed by the purse private 
key.
+Package: GNU Taler
+
+Number: 1211
+Name: WALLET_PURSE_DEPOSIT
+Comment: Request coin to be deposited into a purse. Signed with the coin 
private key.
+Package: GNU Taler
+
+Number: 1212
+Name: WALLET_PURSE_STATUS
+Comment: Request purse status. Signed with the purse private key.
+Package: GNU Taler
+
+Number: 1213
+Name: WALLET_PURSE_MERGE
+Comment: Request purse to be merged with a reserve. Signed with the purse 
private key.
+Package: GNU Taler
+
+Number: 1214
+Name: WALLET_ACCOUNT_MERGE
+Comment: Request purse to be merged with a reserve. Signed by the reserve 
private key.
+Package: GNU Taler
+
+Number: 1215
+Name: WALLET_RESERVE_CLOSE
+Comment: Request account to be closed. Signed with the reserve private key.
+Package: GNU Taler
+
+Number: 1216
+Name: WALLET_PURSE_ECONTRACT
+Comment: Associates encrypted contract with a purse. Signed with the purse 
private key.
+Package: GNU Taler
+
+Number: 1217
+Name: WALLET_RESERVE_OPEN
+Comment: Request reserve to be kept open. Signed with the reserve private key.
+Package: GNU Taler
+
+Number: 1218
+Name: WALLET_RESERVE_OPEN_DEPOSIT
+Comment: Request coin to be used to pay for reserve to be kept open. Signed 
with the coin private key.
+Package: GNU Taler
+
+Number: 1219
+Name: WALLET_RESERVE_ATTEST_DETAILS
+Comment: Request attestation about reserve owner. Signed by the reserve 
private key.
+Package: GNU Taler
+
+#
+# Security module signatures
+#
+
+Number: 1250
+Name: SM_RSA_DENOMINATION_KEY
+Comment: Signature on a denomination key announcement.
+Package: GNU Taler
+
+Number: 1251
+Name: SM_SIGNING_KEY
+Comment: Signature on an exchange message signing key announcement.
+Package: GNU Taler
+
+Number: 1252
+Name: SM_CS_DENOMINATION_KEY
+Comment: Signature on a denomination key announcement.
+Package: GNU Taler
+
+#
+# Test signatures
+#
+
+Number: 1302
+Name: CLIENT_TEST_EDDSA
+Comment: EdDSA test signature.
+Package: GNU Taler
+
+Number: 1303
+Name: EXCHANGE_TEST_EDDSA
+Comment: EdDSA test signature.
+Package: GNU Taler
+
+#
+# GNU Anastasis signatures, >= 1400
+#
+
+Number: 1400
+Name: ANASTASIS_POLICY_UPLOAD
+Comment: EdDSA signature for a policy upload.
+Package: GNU Taler
+
+#
+# Sync signatures, >= 1450
+#
+
+Number: 1450
+Name: SYNC_BACKUP_UPLOAD
+Comment: EdDSA signature for a backup upload.
+Package: GNU Taler
diff --git a/src/gnunet/enums/messages.go b/src/gnunet/enums/messages.go
index ca76d3a..400888a 100644
--- a/src/gnunet/enums/messages.go
+++ b/src/gnunet/enums/messages.go
@@ -367,27 +367,31 @@ const (
        // NAMESTORE message types
        //------------------------------------------------------------------
 
-       MSG_NAMESTORE_RECORD_STORE           MsgType = 435 // Client to 
service: store records (as authority)
-       MSG_NAMESTORE_RECORD_STORE_RESPONSE  MsgType = 436 // Service to 
client: result of store operation.
-       MSG_NAMESTORE_RECORD_LOOKUP          MsgType = 437 // Client to 
service: lookup label
-       MSG_NAMESTORE_RECORD_LOOKUP_RESPONSE MsgType = 438 // Service to 
client: lookup label
-       MSG_NAMESTORE_ZONE_TO_NAME           MsgType = 439 // Client to 
service: "reverse" lookup for zone name based on zone key
-       MSG_NAMESTORE_ZONE_TO_NAME_RESPONSE  MsgType = 440 // Service to 
client: result of zone-to-name lookup.
-       MSG_NAMESTORE_MONITOR_START          MsgType = 441 // Client to 
service: start monitoring (yields sequence of "ZONE_ITERATION_RESPONSES" --- 
forever).
-       MSG_NAMESTORE_MONITOR_SYNC           MsgType = 442 // Service to 
client: you're now in sync.
-       MSG_NAMESTORE_RECORD_RESULT          MsgType = 443 // Service to 
client: here is a (plaintext) record you requested.
-       MSG_NAMESTORE_MONITOR_NEXT           MsgType = 444 // Client to 
service: I am now ready for the next (set of) monitor events. Monitoring 
equivalent of #NAMESTORE_ZONE_ITERATION_NEXT.
-       MSG_NAMESTORE_ZONE_ITERATION_START   MsgType = 445 // Client to 
service: please start iteration; receives "NAMESTORE_LOOKUP_NAME_RESPONSE" 
messages in return.
-       MSG_NAMESTORE_ZONE_ITERATION_NEXT    MsgType = 447 // Client to 
service: next record(s) in iteration please.
-       MSG_NAMESTORE_ZONE_ITERATION_STOP    MsgType = 448 // Client to 
service: stop iterating.
+       MSG_NAMESTORE_RECORD_STORE           MsgType = 435  // Client to 
service: store records (as authority)
+       MSG_NAMESTORE_RECORD_STORE_RESPONSE  MsgType = 436  // Service to 
client: result of store operation.
+       MSG_NAMESTORE_RECORD_LOOKUP          MsgType = 437  // Client to 
service: lookup label
+       MSG_NAMESTORE_RECORD_LOOKUP_RESPONSE MsgType = 438  // Service to 
client: lookup label
+       MSG_NAMESTORE_ZONE_TO_NAME           MsgType = 439  // Client to 
service: "reverse" lookup for zone name based on zone key
+       MSG_NAMESTORE_ZONE_TO_NAME_RESPONSE  MsgType = 440  // Service to 
client: result of zone-to-name lookup.
+       MSG_NAMESTORE_MONITOR_START          MsgType = 441  // Client to 
service: start monitoring (yields sequence of "ZONE_ITERATION_RESPONSES" --- 
forever).
+       MSG_NAMESTORE_MONITOR_SYNC           MsgType = 442  // Service to 
client: you're now in sync.
+       MSG_NAMESTORE_RECORD_RESULT          MsgType = 443  // Service to 
client: here is a (plaintext) record you requested.
+       MSG_NAMESTORE_MONITOR_NEXT           MsgType = 444  // Client to 
service: I am now ready for the next (set of) monitor events. Monitoring 
equivalent of #NAMESTORE_ZONE_ITERATION_NEXT.
+       MSG_NAMESTORE_ZONE_ITERATION_START   MsgType = 445  // Client to 
service: please start iteration; receives "NAMESTORE_LOOKUP_NAME_RESPONSE" 
messages in return.
+       MSG_NAMESTORE_ZONE_ITERATION_NEXT    MsgType = 447  // Client to 
service: next record(s) in iteration please.
+       MSG_NAMESTORE_ZONE_ITERATION_STOP    MsgType = 448  // Client to 
service: stop iterating.
+       MSG_NAMESTORE_ZONE_ITERATION_END     MsgType = 449  // Service to 
client: done iterating.
+       MSG_NAMESTORE_TX_CONTROL             MsgType = 1750 // Begin, Commit or 
Rollback
+       MSG_NAMESTORE_TX_CONTROL_RESULT      MsgType = 1751 // status message 
for control message
+       MSG_NAMESTORE_RECORD_EDIT            MsgType = 1752 // open and lock 
records for editing message
 
        //------------------------------------------------------------------
        // LOCKMANAGER message types
        //------------------------------------------------------------------
 
-       MSG_LOCKMANAGER_ACQUIREMsgType = 450 // Message to acquire Lock
-       MSG_LOCKMANAGER_RELEASEMsgType = 451 // Message to release lock
-       MSG_LOCKMANAGER_SUCCESSMsgType = 452 // SUCCESS reply from lockmanager
+       MSG_LOCKMANAGER_ACQUIREMsgType MsgType = 450 // Message to acquire Lock
+       MSG_LOCKMANAGER_RELEASEMsgType MsgType = 451 // Message to release lock
+       MSG_LOCKMANAGER_SUCCESSMsgType MsgType = 452 // SUCCESS reply from 
lockmanager
 
        //------------------------------------------------------------------
        // TESTBED message types
diff --git a/src/gnunet/enums/msgtype_string.go 
b/src/gnunet/enums/msgtype_string.go
index bab1e22..2d2b308 100644
--- a/src/gnunet/enums/msgtype_string.go
+++ b/src/gnunet/enums/msgtype_string.go
@@ -232,6 +232,13 @@ func _() {
        _ = x[MSG_NAMESTORE_ZONE_ITERATION_START-445]
        _ = x[MSG_NAMESTORE_ZONE_ITERATION_NEXT-447]
        _ = x[MSG_NAMESTORE_ZONE_ITERATION_STOP-448]
+       _ = x[MSG_NAMESTORE_ZONE_ITERATION_END-449]
+       _ = x[MSG_NAMESTORE_TX_CONTROL-1750]
+       _ = x[MSG_NAMESTORE_TX_CONTROL_RESULT-1751]
+       _ = x[MSG_NAMESTORE_RECORD_EDIT-1752]
+       _ = x[MSG_LOCKMANAGER_ACQUIREMsgType-450]
+       _ = x[MSG_LOCKMANAGER_RELEASEMsgType-451]
+       _ = x[MSG_LOCKMANAGER_SUCCESSMsgType-452]
        _ = x[MSG_TESTBED_INIT-460]
        _ = x[MSG_TESTBED_ADD_HOST-461]
        _ = x[MSG_TESTBED_ADD_HOST_SUCCESS-462]
@@ -556,7 +563,7 @@ func _() {
        _ = x[MSG_ALL-65535]
 }
 
-const _MsgType_name = 
"MSG_TESTMSG_DUMMYMSG_DUMMY2MSG_RESOLVER_REQUESTMSG_RESOLVER_RESPONSEMSG_REQUEST_AGPLMSG_RESPONSE_AGPLMSG_ARM_STARTMSG_ARM_STOPMSG_ARM_RESULTMSG_ARM_STATUSMSG_ARM_LISTMSG_ARM_LIST_RESULTMSG_ARM_MONITORMSG_ARM_TESTMSG_HELLO_LEGACYMSG_HELLOMSG_FRAGMENTMSG_FRAGMENT_ACKMSG_WLAN_DATA_TO_HELPERMSG_WLAN_DATA_FROM_HELPERMSG_WLAN_HELPER_CONTROLMSG_WLAN_ADVERTISEMENTMSG_WLAN_DATAMSG_DV_RECVMSG_DV_SENDMSG_DV_SEND_ACKMSG_DV_ROUTEMSG_DV_STARTMSG_DV_CONNECTMSG_DV_DISCONNECTMSG_DV
 [...]
+const _MsgType_name = 
"MSG_TESTMSG_DUMMYMSG_DUMMY2MSG_RESOLVER_REQUESTMSG_RESOLVER_RESPONSEMSG_REQUEST_AGPLMSG_RESPONSE_AGPLMSG_ARM_STARTMSG_ARM_STOPMSG_ARM_RESULTMSG_ARM_STATUSMSG_ARM_LISTMSG_ARM_LIST_RESULTMSG_ARM_MONITORMSG_ARM_TESTMSG_HELLO_LEGACYMSG_HELLOMSG_FRAGMENTMSG_FRAGMENT_ACKMSG_WLAN_DATA_TO_HELPERMSG_WLAN_DATA_FROM_HELPERMSG_WLAN_HELPER_CONTROLMSG_WLAN_ADVERTISEMENTMSG_WLAN_DATAMSG_DV_RECVMSG_DV_SENDMSG_DV_SEND_ACKMSG_DV_ROUTEMSG_DV_STARTMSG_DV_CONNECTMSG_DV_DISCONNECTMSG_DV
 [...]
 
 var _MsgType_map = map[MsgType]string{
        1:     _MsgType_name[0:8],
@@ -783,326 +790,333 @@ var _MsgType_map = map[MsgType]string{
        445:   _MsgType_name[4976:5010],
        447:   _MsgType_name[5010:5043],
        448:   _MsgType_name[5043:5076],
-       460:   _MsgType_name[5076:5092],
-       461:   _MsgType_name[5092:5112],
-       462:   _MsgType_name[5112:5140],
-       463:   _MsgType_name[5140:5168],
-       464:   _MsgType_name[5168:5191],
-       465:   _MsgType_name[5191:5219],
-       466:   _MsgType_name[5219:5241],
-       467:   _MsgType_name[5241:5262],
-       468:   _MsgType_name[5262:5286],
-       469:   _MsgType_name[5286:5321],
-       470:   _MsgType_name[5321:5348],
-       471:   _MsgType_name[5348:5370],
-       472:   _MsgType_name[5370:5400],
-       473:   _MsgType_name[5400:5432],
-       474:   _MsgType_name[5432:5463],
-       475:   _MsgType_name[5463:5500],
-       476:   _MsgType_name[5500:5532],
-       477:   _MsgType_name[5532:5560],
-       478:   _MsgType_name[5560:5594],
-       479:   _MsgType_name[5594:5629],
-       480:   _MsgType_name[5629:5660],
-       481:   _MsgType_name[5660:5695],
-       482:   _MsgType_name[5695:5721],
-       483:   _MsgType_name[5721:5752],
-       484:   _MsgType_name[5752:5776],
-       485:   _MsgType_name[5776:5802],
-       486:   _MsgType_name[5802:5828],
-       487:   _MsgType_name[5828:5852],
-       488:   _MsgType_name[5852:5867],
-       495:   _MsgType_name[5867:5890],
-       496:   _MsgType_name[5890:5914],
-       500:   _MsgType_name[5914:5928],
-       501:   _MsgType_name[5928:5949],
-       502:   _MsgType_name[5949:5971],
-       503:   _MsgType_name[5971:6000],
-       520:   _MsgType_name[6000:6025],
-       521:   _MsgType_name[6025:6052],
-       522:   _MsgType_name[6052:6078],
-       523:   _MsgType_name[6078:6115],
-       524:   _MsgType_name[6115:6144],
-       525:   _MsgType_name[6144:6178],
-       540:   _MsgType_name[6178:6202],
-       541:   _MsgType_name[6202:6234],
-       542:   _MsgType_name[6234:6269],
-       543:   _MsgType_name[6269:6295],
-       544:   _MsgType_name[6295:6329],
-       545:   _MsgType_name[6329:6362],
-       546:   _MsgType_name[6362:6385],
-       547:   _MsgType_name[6385:6409],
-       548:   _MsgType_name[6409:6430],
-       565:   _MsgType_name[6430:6460],
-       566:   _MsgType_name[6460:6484],
-       567:   _MsgType_name[6484:6509],
-       568:   _MsgType_name[6509:6532],
-       569:   _MsgType_name[6532:6546],
-       570:   _MsgType_name[6546:6560],
-       571:   _MsgType_name[6560:6576],
-       572:   _MsgType_name[6576:6590],
-       573:   _MsgType_name[6590:6601],
-       574:   _MsgType_name[6601:6615],
-       575:   _MsgType_name[6615:6629],
-       576:   _MsgType_name[6629:6643],
-       577:   _MsgType_name[6643:6659],
-       578:   _MsgType_name[6659:6675],
-       579:   _MsgType_name[6675:6690],
-       580:   _MsgType_name[6690:6704],
-       581:   _MsgType_name[6704:6733],
-       582:   _MsgType_name[6733:6753],
-       583:   _MsgType_name[6753:6774],
-       584:   _MsgType_name[6774:6794],
-       585:   _MsgType_name[6794:6822],
-       586:   _MsgType_name[6822:6844],
-       587:   _MsgType_name[6844:6864],
-       588:   _MsgType_name[6864:6884],
-       589:   _MsgType_name[6884:6901],
-       590:   _MsgType_name[6901:6922],
-       591:   _MsgType_name[6922:6959],
-       592:   _MsgType_name[6959:6986],
-       593:   _MsgType_name[6986:7015],
-       594:   _MsgType_name[7015:7040],
-       595:   _MsgType_name[7040:7066],
-       596:   _MsgType_name[7066:7091],
-       597:   _MsgType_name[7091:7118],
-       598:   _MsgType_name[7118:7148],
-       599:   _MsgType_name[7148:7170],
-       600:   _MsgType_name[7170:7192],
-       601:   _MsgType_name[7192:7214],
-       620:   _MsgType_name[7214:7232],
-       621:   _MsgType_name[7232:7248],
-       622:   _MsgType_name[7248:7264],
-       624:   _MsgType_name[7264:7282],
-       625:   _MsgType_name[7282:7306],
-       626:   _MsgType_name[7306:7325],
-       627:   _MsgType_name[7325:7349],
-       628:   _MsgType_name[7349:7373],
-       629:   _MsgType_name[7373:7392],
-       630:   _MsgType_name[7392:7411],
-       631:   _MsgType_name[7411:7430],
-       632:   _MsgType_name[7430:7449],
-       633:   _MsgType_name[7449:7476],
-       636:   _MsgType_name[7476:7496],
-       637:   _MsgType_name[7496:7525],
-       638:   _MsgType_name[7525:7546],
-       639:   _MsgType_name[7546:7576],
-       640:   _MsgType_name[7576:7609],
-       641:   _MsgType_name[7609:7640],
-       642:   _MsgType_name[7640:7680],
-       643:   _MsgType_name[7680:7718],
-       644:   _MsgType_name[7718:7758],
-       645:   _MsgType_name[7758:7792],
-       647:   _MsgType_name[7792:7824],
-       648:   _MsgType_name[7824:7866],
-       649:   _MsgType_name[7866:7890],
-       650:   _MsgType_name[7890:7934],
-       651:   _MsgType_name[7934:7972],
-       652:   _MsgType_name[7972:8008],
-       660:   _MsgType_name[8008:8038],
-       661:   _MsgType_name[8038:8067],
-       662:   _MsgType_name[8067:8095],
-       663:   _MsgType_name[8095:8121],
-       664:   _MsgType_name[8121:8146],
-       665:   _MsgType_name[8146:8180],
-       666:   _MsgType_name[8180:8206],
-       668:   _MsgType_name[8206:8232],
-       669:   _MsgType_name[8232:8256],
-       670:   _MsgType_name[8256:8281],
-       671:   _MsgType_name[8281:8312],
-       672:   _MsgType_name[8312:8335],
-       673:   _MsgType_name[8335:8365],
-       674:   _MsgType_name[8365:8390],
-       675:   _MsgType_name[8390:8419],
-       676:   _MsgType_name[8419:8448],
-       677:   _MsgType_name[8448:8474],
-       680:   _MsgType_name[8474:8494],
-       681:   _MsgType_name[8494:8515],
-       682:   _MsgType_name[8515:8540],
-       683:   _MsgType_name[8540:8559],
-       684:   _MsgType_name[8559:8582],
-       685:   _MsgType_name[8582:8603],
-       686:   _MsgType_name[8603:8620],
-       687:   _MsgType_name[8620:8641],
-       688:   _MsgType_name[8641:8663],
-       689:   _MsgType_name[8663:8696],
-       691:   _MsgType_name[8696:8712],
-       692:   _MsgType_name[8712:8735],
-       693:   _MsgType_name[8735:8758],
-       694:   _MsgType_name[8758:8783],
-       695:   _MsgType_name[8783:8808],
-       696:   _MsgType_name[8808:8829],
-       697:   _MsgType_name[8829:8849],
-       698:   _MsgType_name[8849:8872],
-       699:   _MsgType_name[8872:8892],
-       701:   _MsgType_name[8892:8915],
-       702:   _MsgType_name[8915:8938],
-       703:   _MsgType_name[8938:8956],
-       704:   _MsgType_name[8956:8981],
-       705:   _MsgType_name[8981:9002],
-       730:   _MsgType_name[9002:9024],
-       731:   _MsgType_name[9024:9058],
-       732:   _MsgType_name[9058:9091],
-       733:   _MsgType_name[9091:9124],
-       734:   _MsgType_name[9124:9154],
-       735:   _MsgType_name[9154:9184],
-       736:   _MsgType_name[9184:9217],
-       737:   _MsgType_name[9217:9249],
-       738:   _MsgType_name[9249:9284],
-       739:   _MsgType_name[9284:9309],
-       740:   _MsgType_name[9309:9342],
-       741:   _MsgType_name[9342:9378],
-       742:   _MsgType_name[9378:9414],
-       743:   _MsgType_name[9414:9450],
-       744:   _MsgType_name[9450:9485],
-       745:   _MsgType_name[9485:9513],
-       750:   _MsgType_name[9513:9539],
-       751:   _MsgType_name[9539:9564],
-       752:   _MsgType_name[9564:9590],
-       753:   _MsgType_name[9590:9617],
-       754:   _MsgType_name[9617:9643],
-       755:   _MsgType_name[9643:9665],
-       756:   _MsgType_name[9665:9688],
-       757:   _MsgType_name[9688:9709],
-       758:   _MsgType_name[9709:9730],
-       759:   _MsgType_name[9730:9756],
-       760:   _MsgType_name[9756:9784],
-       761:   _MsgType_name[9784:9813],
-       762:   _MsgType_name[9813:9846],
-       780:   _MsgType_name[9846:9879],
-       781:   _MsgType_name[9879:9911],
-       782:   _MsgType_name[9911:9948],
-       783:   _MsgType_name[9948:9985],
-       820:   _MsgType_name[9985:10004],
-       821:   _MsgType_name[10004:10025],
-       822:   _MsgType_name[10025:10053],
-       823:   _MsgType_name[10053:10078],
-       824:   _MsgType_name[10078:10097],
-       825:   _MsgType_name[10097:10123],
-       826:   _MsgType_name[10123:10149],
-       840:   _MsgType_name[10149:10171],
-       841:   _MsgType_name[10171:10192],
-       842:   _MsgType_name[10192:10217],
-       843:   _MsgType_name[10217:10239],
-       844:   _MsgType_name[10239:10269],
-       845:   _MsgType_name[10269:10295],
-       846:   _MsgType_name[10295:10319],
-       847:   _MsgType_name[10319:10344],
-       848:   _MsgType_name[10344:10366],
-       849:   _MsgType_name[10366:10392],
-       850:   _MsgType_name[10392:10417],
-       851:   _MsgType_name[10417:10440],
-       852:   _MsgType_name[10440:10462],
-       853:   _MsgType_name[10462:10483],
-       854:   _MsgType_name[10483:10501],
-       855:   _MsgType_name[10501:10523],
-       856:   _MsgType_name[10523:10543],
-       857:   _MsgType_name[10543:10567],
-       858:   _MsgType_name[10567:10590],
-       859:   _MsgType_name[10590:10615],
-       880:   _MsgType_name[10615:10639],
-       881:   _MsgType_name[10639:10670],
-       882:   _MsgType_name[10670:10699],
-       883:   _MsgType_name[10699:10732],
-       884:   _MsgType_name[10732:10768],
-       885:   _MsgType_name[10768:10791],
-       886:   _MsgType_name[10791:10825],
-       887:   _MsgType_name[10825:10852],
-       888:   _MsgType_name[10852:10874],
-       890:   _MsgType_name[10874:10890],
-       891:   _MsgType_name[10890:10906],
-       892:   _MsgType_name[10906:10948],
-       893:   _MsgType_name[10948:10969],
-       894:   _MsgType_name[10969:11000],
-       910:   _MsgType_name[11000:11020],
-       911:   _MsgType_name[11020:11049],
-       912:   _MsgType_name[11049:11071],
-       913:   _MsgType_name[11071:11091],
-       914:   _MsgType_name[11091:11114],
-       915:   _MsgType_name[11114:11126],
-       916:   _MsgType_name[11126:11138],
-       917:   _MsgType_name[11138:11157],
-       950:   _MsgType_name[11157:11178],
-       951:   _MsgType_name[11178:11193],
-       952:   _MsgType_name[11193:11216],
-       953:   _MsgType_name[11216:11237],
-       954:   _MsgType_name[11237:11252],
-       955:   _MsgType_name[11252:11273],
-       956:   _MsgType_name[11273:11293],
-       957:   _MsgType_name[11293:11312],
-       961:   _MsgType_name[11312:11339],
-       962:   _MsgType_name[11339:11367],
-       963:   _MsgType_name[11367:11404],
-       964:   _MsgType_name[11404:11440],
-       965:   _MsgType_name[11440:11476],
-       966:   _MsgType_name[11476:11504],
-       967:   _MsgType_name[11504:11528],
-       968:   _MsgType_name[11528:11553],
-       969:   _MsgType_name[11553:11578],
-       970:   _MsgType_name[11578:11610],
-       971:   _MsgType_name[11610:11636],
-       972:   _MsgType_name[11636:11669],
-       973:   _MsgType_name[11669:11703],
-       974:   _MsgType_name[11703:11736],
-       975:   _MsgType_name[11736:11769],
-       976:   _MsgType_name[11769:11797],
-       981:   _MsgType_name[11797:11818],
-       982:   _MsgType_name[11818:11846],
-       983:   _MsgType_name[11846:11868],
-       984:   _MsgType_name[11868:11897],
-       1000:  _MsgType_name[11897:11924],
-       1001:  _MsgType_name[11924:11955],
-       1002:  _MsgType_name[11955:11982],
-       1003:  _MsgType_name[11982:12010],
-       1004:  _MsgType_name[12010:12057],
-       1005:  _MsgType_name[12057:12102],
-       1006:  _MsgType_name[12102:12133],
-       1007:  _MsgType_name[12133:12152],
-       1008:  _MsgType_name[12152:12178],
-       1009:  _MsgType_name[12178:12202],
-       1010:  _MsgType_name[12202:12228],
-       1011:  _MsgType_name[12228:12258],
-       1012:  _MsgType_name[12258:12285],
-       1013:  _MsgType_name[12285:12307],
-       1014:  _MsgType_name[12307:12332],
-       1015:  _MsgType_name[12332:12358],
-       1016:  _MsgType_name[12358:12396],
-       1020:  _MsgType_name[12396:12416],
-       1021:  _MsgType_name[12416:12435],
-       1022:  _MsgType_name[12435:12460],
-       1023:  _MsgType_name[12460:12486],
-       1024:  _MsgType_name[12486:12516],
-       1025:  _MsgType_name[12516:12547],
-       1030:  _MsgType_name[12547:12583],
-       1031:  _MsgType_name[12583:12611],
-       1032:  _MsgType_name[12611:12643],
-       1033:  _MsgType_name[12643:12677],
-       1034:  _MsgType_name[12677:12703],
-       1035:  _MsgType_name[12703:12733],
-       1036:  _MsgType_name[12733:12766],
-       1037:  _MsgType_name[12766:12791],
-       1038:  _MsgType_name[12791:12820],
-       1039:  _MsgType_name[12820:12856],
-       1040:  _MsgType_name[12856:12884],
-       1041:  _MsgType_name[12884:12916],
-       1059:  _MsgType_name[12916:12929],
-       1060:  _MsgType_name[12929:12945],
-       1061:  _MsgType_name[12945:12964],
-       1062:  _MsgType_name[12964:12999],
-       1063:  _MsgType_name[12999:13036],
-       1064:  _MsgType_name[13036:13058],
-       1065:  _MsgType_name[13058:13081],
-       1066:  _MsgType_name[13081:13105],
-       1110:  _MsgType_name[13105:13130],
-       1111:  _MsgType_name[13130:13153],
-       1112:  _MsgType_name[13153:13179],
-       1130:  _MsgType_name[13179:13208],
-       1131:  _MsgType_name[13208:13235],
-       1132:  _MsgType_name[13235:13263],
-       1133:  _MsgType_name[13263:13294],
-       1134:  _MsgType_name[13294:13323],
-       1135:  _MsgType_name[13323:13353],
-       65535: _MsgType_name[13353:13360],
+       449:   _MsgType_name[5076:5108],
+       450:   _MsgType_name[5108:5138],
+       451:   _MsgType_name[5138:5168],
+       452:   _MsgType_name[5168:5198],
+       460:   _MsgType_name[5198:5214],
+       461:   _MsgType_name[5214:5234],
+       462:   _MsgType_name[5234:5262],
+       463:   _MsgType_name[5262:5290],
+       464:   _MsgType_name[5290:5313],
+       465:   _MsgType_name[5313:5341],
+       466:   _MsgType_name[5341:5363],
+       467:   _MsgType_name[5363:5384],
+       468:   _MsgType_name[5384:5408],
+       469:   _MsgType_name[5408:5443],
+       470:   _MsgType_name[5443:5470],
+       471:   _MsgType_name[5470:5492],
+       472:   _MsgType_name[5492:5522],
+       473:   _MsgType_name[5522:5554],
+       474:   _MsgType_name[5554:5585],
+       475:   _MsgType_name[5585:5622],
+       476:   _MsgType_name[5622:5654],
+       477:   _MsgType_name[5654:5682],
+       478:   _MsgType_name[5682:5716],
+       479:   _MsgType_name[5716:5751],
+       480:   _MsgType_name[5751:5782],
+       481:   _MsgType_name[5782:5817],
+       482:   _MsgType_name[5817:5843],
+       483:   _MsgType_name[5843:5874],
+       484:   _MsgType_name[5874:5898],
+       485:   _MsgType_name[5898:5924],
+       486:   _MsgType_name[5924:5950],
+       487:   _MsgType_name[5950:5974],
+       488:   _MsgType_name[5974:5989],
+       495:   _MsgType_name[5989:6012],
+       496:   _MsgType_name[6012:6036],
+       500:   _MsgType_name[6036:6050],
+       501:   _MsgType_name[6050:6071],
+       502:   _MsgType_name[6071:6093],
+       503:   _MsgType_name[6093:6122],
+       520:   _MsgType_name[6122:6147],
+       521:   _MsgType_name[6147:6174],
+       522:   _MsgType_name[6174:6200],
+       523:   _MsgType_name[6200:6237],
+       524:   _MsgType_name[6237:6266],
+       525:   _MsgType_name[6266:6300],
+       540:   _MsgType_name[6300:6324],
+       541:   _MsgType_name[6324:6356],
+       542:   _MsgType_name[6356:6391],
+       543:   _MsgType_name[6391:6417],
+       544:   _MsgType_name[6417:6451],
+       545:   _MsgType_name[6451:6484],
+       546:   _MsgType_name[6484:6507],
+       547:   _MsgType_name[6507:6531],
+       548:   _MsgType_name[6531:6552],
+       565:   _MsgType_name[6552:6582],
+       566:   _MsgType_name[6582:6606],
+       567:   _MsgType_name[6606:6631],
+       568:   _MsgType_name[6631:6654],
+       569:   _MsgType_name[6654:6668],
+       570:   _MsgType_name[6668:6682],
+       571:   _MsgType_name[6682:6698],
+       572:   _MsgType_name[6698:6712],
+       573:   _MsgType_name[6712:6723],
+       574:   _MsgType_name[6723:6737],
+       575:   _MsgType_name[6737:6751],
+       576:   _MsgType_name[6751:6765],
+       577:   _MsgType_name[6765:6781],
+       578:   _MsgType_name[6781:6797],
+       579:   _MsgType_name[6797:6812],
+       580:   _MsgType_name[6812:6826],
+       581:   _MsgType_name[6826:6855],
+       582:   _MsgType_name[6855:6875],
+       583:   _MsgType_name[6875:6896],
+       584:   _MsgType_name[6896:6916],
+       585:   _MsgType_name[6916:6944],
+       586:   _MsgType_name[6944:6966],
+       587:   _MsgType_name[6966:6986],
+       588:   _MsgType_name[6986:7006],
+       589:   _MsgType_name[7006:7023],
+       590:   _MsgType_name[7023:7044],
+       591:   _MsgType_name[7044:7081],
+       592:   _MsgType_name[7081:7108],
+       593:   _MsgType_name[7108:7137],
+       594:   _MsgType_name[7137:7162],
+       595:   _MsgType_name[7162:7188],
+       596:   _MsgType_name[7188:7213],
+       597:   _MsgType_name[7213:7240],
+       598:   _MsgType_name[7240:7270],
+       599:   _MsgType_name[7270:7292],
+       600:   _MsgType_name[7292:7314],
+       601:   _MsgType_name[7314:7336],
+       620:   _MsgType_name[7336:7354],
+       621:   _MsgType_name[7354:7370],
+       622:   _MsgType_name[7370:7386],
+       624:   _MsgType_name[7386:7404],
+       625:   _MsgType_name[7404:7428],
+       626:   _MsgType_name[7428:7447],
+       627:   _MsgType_name[7447:7471],
+       628:   _MsgType_name[7471:7495],
+       629:   _MsgType_name[7495:7514],
+       630:   _MsgType_name[7514:7533],
+       631:   _MsgType_name[7533:7552],
+       632:   _MsgType_name[7552:7571],
+       633:   _MsgType_name[7571:7598],
+       636:   _MsgType_name[7598:7618],
+       637:   _MsgType_name[7618:7647],
+       638:   _MsgType_name[7647:7668],
+       639:   _MsgType_name[7668:7698],
+       640:   _MsgType_name[7698:7731],
+       641:   _MsgType_name[7731:7762],
+       642:   _MsgType_name[7762:7802],
+       643:   _MsgType_name[7802:7840],
+       644:   _MsgType_name[7840:7880],
+       645:   _MsgType_name[7880:7914],
+       647:   _MsgType_name[7914:7946],
+       648:   _MsgType_name[7946:7988],
+       649:   _MsgType_name[7988:8012],
+       650:   _MsgType_name[8012:8056],
+       651:   _MsgType_name[8056:8094],
+       652:   _MsgType_name[8094:8130],
+       660:   _MsgType_name[8130:8160],
+       661:   _MsgType_name[8160:8189],
+       662:   _MsgType_name[8189:8217],
+       663:   _MsgType_name[8217:8243],
+       664:   _MsgType_name[8243:8268],
+       665:   _MsgType_name[8268:8302],
+       666:   _MsgType_name[8302:8328],
+       668:   _MsgType_name[8328:8354],
+       669:   _MsgType_name[8354:8378],
+       670:   _MsgType_name[8378:8403],
+       671:   _MsgType_name[8403:8434],
+       672:   _MsgType_name[8434:8457],
+       673:   _MsgType_name[8457:8487],
+       674:   _MsgType_name[8487:8512],
+       675:   _MsgType_name[8512:8541],
+       676:   _MsgType_name[8541:8570],
+       677:   _MsgType_name[8570:8596],
+       680:   _MsgType_name[8596:8616],
+       681:   _MsgType_name[8616:8637],
+       682:   _MsgType_name[8637:8662],
+       683:   _MsgType_name[8662:8681],
+       684:   _MsgType_name[8681:8704],
+       685:   _MsgType_name[8704:8725],
+       686:   _MsgType_name[8725:8742],
+       687:   _MsgType_name[8742:8763],
+       688:   _MsgType_name[8763:8785],
+       689:   _MsgType_name[8785:8818],
+       691:   _MsgType_name[8818:8834],
+       692:   _MsgType_name[8834:8857],
+       693:   _MsgType_name[8857:8880],
+       694:   _MsgType_name[8880:8905],
+       695:   _MsgType_name[8905:8930],
+       696:   _MsgType_name[8930:8951],
+       697:   _MsgType_name[8951:8971],
+       698:   _MsgType_name[8971:8994],
+       699:   _MsgType_name[8994:9014],
+       701:   _MsgType_name[9014:9037],
+       702:   _MsgType_name[9037:9060],
+       703:   _MsgType_name[9060:9078],
+       704:   _MsgType_name[9078:9103],
+       705:   _MsgType_name[9103:9124],
+       730:   _MsgType_name[9124:9146],
+       731:   _MsgType_name[9146:9180],
+       732:   _MsgType_name[9180:9213],
+       733:   _MsgType_name[9213:9246],
+       734:   _MsgType_name[9246:9276],
+       735:   _MsgType_name[9276:9306],
+       736:   _MsgType_name[9306:9339],
+       737:   _MsgType_name[9339:9371],
+       738:   _MsgType_name[9371:9406],
+       739:   _MsgType_name[9406:9431],
+       740:   _MsgType_name[9431:9464],
+       741:   _MsgType_name[9464:9500],
+       742:   _MsgType_name[9500:9536],
+       743:   _MsgType_name[9536:9572],
+       744:   _MsgType_name[9572:9607],
+       745:   _MsgType_name[9607:9635],
+       750:   _MsgType_name[9635:9661],
+       751:   _MsgType_name[9661:9686],
+       752:   _MsgType_name[9686:9712],
+       753:   _MsgType_name[9712:9739],
+       754:   _MsgType_name[9739:9765],
+       755:   _MsgType_name[9765:9787],
+       756:   _MsgType_name[9787:9810],
+       757:   _MsgType_name[9810:9831],
+       758:   _MsgType_name[9831:9852],
+       759:   _MsgType_name[9852:9878],
+       760:   _MsgType_name[9878:9906],
+       761:   _MsgType_name[9906:9935],
+       762:   _MsgType_name[9935:9968],
+       780:   _MsgType_name[9968:10001],
+       781:   _MsgType_name[10001:10033],
+       782:   _MsgType_name[10033:10070],
+       783:   _MsgType_name[10070:10107],
+       820:   _MsgType_name[10107:10126],
+       821:   _MsgType_name[10126:10147],
+       822:   _MsgType_name[10147:10175],
+       823:   _MsgType_name[10175:10200],
+       824:   _MsgType_name[10200:10219],
+       825:   _MsgType_name[10219:10245],
+       826:   _MsgType_name[10245:10271],
+       840:   _MsgType_name[10271:10293],
+       841:   _MsgType_name[10293:10314],
+       842:   _MsgType_name[10314:10339],
+       843:   _MsgType_name[10339:10361],
+       844:   _MsgType_name[10361:10391],
+       845:   _MsgType_name[10391:10417],
+       846:   _MsgType_name[10417:10441],
+       847:   _MsgType_name[10441:10466],
+       848:   _MsgType_name[10466:10488],
+       849:   _MsgType_name[10488:10514],
+       850:   _MsgType_name[10514:10539],
+       851:   _MsgType_name[10539:10562],
+       852:   _MsgType_name[10562:10584],
+       853:   _MsgType_name[10584:10605],
+       854:   _MsgType_name[10605:10623],
+       855:   _MsgType_name[10623:10645],
+       856:   _MsgType_name[10645:10665],
+       857:   _MsgType_name[10665:10689],
+       858:   _MsgType_name[10689:10712],
+       859:   _MsgType_name[10712:10737],
+       880:   _MsgType_name[10737:10761],
+       881:   _MsgType_name[10761:10792],
+       882:   _MsgType_name[10792:10821],
+       883:   _MsgType_name[10821:10854],
+       884:   _MsgType_name[10854:10890],
+       885:   _MsgType_name[10890:10913],
+       886:   _MsgType_name[10913:10947],
+       887:   _MsgType_name[10947:10974],
+       888:   _MsgType_name[10974:10996],
+       890:   _MsgType_name[10996:11012],
+       891:   _MsgType_name[11012:11028],
+       892:   _MsgType_name[11028:11070],
+       893:   _MsgType_name[11070:11091],
+       894:   _MsgType_name[11091:11122],
+       910:   _MsgType_name[11122:11142],
+       911:   _MsgType_name[11142:11171],
+       912:   _MsgType_name[11171:11193],
+       913:   _MsgType_name[11193:11213],
+       914:   _MsgType_name[11213:11236],
+       915:   _MsgType_name[11236:11248],
+       916:   _MsgType_name[11248:11260],
+       917:   _MsgType_name[11260:11279],
+       950:   _MsgType_name[11279:11300],
+       951:   _MsgType_name[11300:11315],
+       952:   _MsgType_name[11315:11338],
+       953:   _MsgType_name[11338:11359],
+       954:   _MsgType_name[11359:11374],
+       955:   _MsgType_name[11374:11395],
+       956:   _MsgType_name[11395:11415],
+       957:   _MsgType_name[11415:11434],
+       961:   _MsgType_name[11434:11461],
+       962:   _MsgType_name[11461:11489],
+       963:   _MsgType_name[11489:11526],
+       964:   _MsgType_name[11526:11562],
+       965:   _MsgType_name[11562:11598],
+       966:   _MsgType_name[11598:11626],
+       967:   _MsgType_name[11626:11650],
+       968:   _MsgType_name[11650:11675],
+       969:   _MsgType_name[11675:11700],
+       970:   _MsgType_name[11700:11732],
+       971:   _MsgType_name[11732:11758],
+       972:   _MsgType_name[11758:11791],
+       973:   _MsgType_name[11791:11825],
+       974:   _MsgType_name[11825:11858],
+       975:   _MsgType_name[11858:11891],
+       976:   _MsgType_name[11891:11919],
+       981:   _MsgType_name[11919:11940],
+       982:   _MsgType_name[11940:11968],
+       983:   _MsgType_name[11968:11990],
+       984:   _MsgType_name[11990:12019],
+       1000:  _MsgType_name[12019:12046],
+       1001:  _MsgType_name[12046:12077],
+       1002:  _MsgType_name[12077:12104],
+       1003:  _MsgType_name[12104:12132],
+       1004:  _MsgType_name[12132:12179],
+       1005:  _MsgType_name[12179:12224],
+       1006:  _MsgType_name[12224:12255],
+       1007:  _MsgType_name[12255:12274],
+       1008:  _MsgType_name[12274:12300],
+       1009:  _MsgType_name[12300:12324],
+       1010:  _MsgType_name[12324:12350],
+       1011:  _MsgType_name[12350:12380],
+       1012:  _MsgType_name[12380:12407],
+       1013:  _MsgType_name[12407:12429],
+       1014:  _MsgType_name[12429:12454],
+       1015:  _MsgType_name[12454:12480],
+       1016:  _MsgType_name[12480:12518],
+       1020:  _MsgType_name[12518:12538],
+       1021:  _MsgType_name[12538:12557],
+       1022:  _MsgType_name[12557:12582],
+       1023:  _MsgType_name[12582:12608],
+       1024:  _MsgType_name[12608:12638],
+       1025:  _MsgType_name[12638:12669],
+       1030:  _MsgType_name[12669:12705],
+       1031:  _MsgType_name[12705:12733],
+       1032:  _MsgType_name[12733:12765],
+       1033:  _MsgType_name[12765:12799],
+       1034:  _MsgType_name[12799:12825],
+       1035:  _MsgType_name[12825:12855],
+       1036:  _MsgType_name[12855:12888],
+       1037:  _MsgType_name[12888:12913],
+       1038:  _MsgType_name[12913:12942],
+       1039:  _MsgType_name[12942:12978],
+       1040:  _MsgType_name[12978:13006],
+       1041:  _MsgType_name[13006:13038],
+       1059:  _MsgType_name[13038:13051],
+       1060:  _MsgType_name[13051:13067],
+       1061:  _MsgType_name[13067:13086],
+       1062:  _MsgType_name[13086:13121],
+       1063:  _MsgType_name[13121:13158],
+       1064:  _MsgType_name[13158:13180],
+       1065:  _MsgType_name[13180:13203],
+       1066:  _MsgType_name[13203:13227],
+       1110:  _MsgType_name[13227:13252],
+       1111:  _MsgType_name[13252:13275],
+       1112:  _MsgType_name[13275:13301],
+       1130:  _MsgType_name[13301:13330],
+       1131:  _MsgType_name[13330:13357],
+       1132:  _MsgType_name[13357:13385],
+       1133:  _MsgType_name[13385:13416],
+       1134:  _MsgType_name[13416:13445],
+       1135:  _MsgType_name[13445:13475],
+       1750:  _MsgType_name[13475:13499],
+       1751:  _MsgType_name[13499:13530],
+       1752:  _MsgType_name[13530:13555],
+       65535: _MsgType_name[13555:13562],
 }
 
 func (i MsgType) String() string {
diff --git a/src/gnunet/enums/signature.go b/src/gnunet/enums/signature.go
deleted file mode 100644
index 0c5cd5c..0000000
--- a/src/gnunet/enums/signature.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// This file is part of gnunet-go, a GNUnet-implementation in Golang.
-// Copyright (C) 2019-2022 Bernd Fix  >Y<
-//
-// gnunet-go is free software: you can redistribute it and/or modify it
-// under the terms of the GNU Affero General Public License as published
-// by the Free Software Foundation, either version 3 of the License,
-// or (at your option) any later version.
-//
-// gnunet-go is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-// SPDX-License-Identifier: AGPL3.0-or-later
-
-package enums
-
-//go:generate go run generate.go gnunet-signature.rec gnunet-signature.tpl 
signature_purpose.go
-
-//go:generate stringer -type=SigPurpose signature_purpose.go
diff --git a/src/gnunet/enums/signature_purpose.go 
b/src/gnunet/enums/signature_purpose.go
index 31f6eca..89e9f45 100644
--- a/src/gnunet/enums/signature_purpose.go
+++ b/src/gnunet/enums/signature_purpose.go
@@ -39,5 +39,6 @@ SIG_TRANSPORT_CHALLENGE SigPurpose = 35 // Signature by a 
peer affirming that it
 SIG_TRANSPORT_DV_HOP SigPurpose = 36 // Signature by a peer affirming that it 
is on a DV path.
 SIG_TRANSPORT_DV_INITIATOR SigPurpose = 37 // Signature by a peer affirming 
that it originated the DV path.
 SIG_CADET_CONNECTION_INITIATOR SigPurpose = 38 // Signature by a peer that 
like to create a connection.
+SIG_COMMUNICATOR_TCP_HANDSHAKE_ACK SigPurpose = 39 // Signature by a peer 
sending back the nonce received at initial handshake.
 
 )
diff --git a/src/gnunet/enums/sigpurpose_string.go 
b/src/gnunet/enums/sigpurpose_string.go
index dd392ab..4c6111c 100644
--- a/src/gnunet/enums/sigpurpose_string.go
+++ b/src/gnunet/enums/sigpurpose_string.go
@@ -40,20 +40,21 @@ func _() {
        _ = x[SIG_TRANSPORT_DV_HOP-36]
        _ = x[SIG_TRANSPORT_DV_INITIATOR-37]
        _ = x[SIG_CADET_CONNECTION_INITIATOR-38]
+       _ = x[SIG_COMMUNICATOR_TCP_HANDSHAKE_ACK-39]
 }
 
 const (
        _SigPurpose_name_0 = 
"SIG_TESTSIG_TRANSPORT_PONG_OWNSIG_TRANSPORT_DISCONNECTSIG_REVOCATIONSIG_NAMESPACE_ADVERTISEMENTSIG_PEER_PLACEMENTSIG_DHT_HOPSIG_HELLO"
        _SigPurpose_name_1 = 
"SIG_DNS_RECORDSIG_CHAT_MESSAGESIG_CHAT_RECEIPTSIG_NSE_SENDSIG_GNS_RECORD_SIGNSIG_SET_ECC_KEYSIG_FS_UBLOCKSIG_REGEX_ACCEPT"
        _SigPurpose_name_2 = 
"SIG_CONVERSATION_RINGSIG_SECRETSHARING_DKG1SIG_SECRETSHARING_DKG2SIG_SECRETSHARING_DECRYPTION"
-       _SigPurpose_name_3 = 
"SIG_RECLAIM_CODE_SIGNSIG_DELEGATESIG_TRANSPORT_ADDRESSSIG_TRANSPORT_EPHEMERALSIG_COMMUNICATOR_TCP_HANDSHAKESIG_COMMUNICATOR_TCP_REKEYSIG_COMMUNICATOR_UDP_HANDSHAKESIG_COMMUNICATOR_UDP_BROADCASTSIG_TRANSPORT_CHALLENGESIG_TRANSPORT_DV_HOPSIG_TRANSPORT_DV_INITIATORSIG_CADET_CONNECTION_INITIATOR"
+       _SigPurpose_name_3 = 
"SIG_RECLAIM_CODE_SIGNSIG_DELEGATESIG_TRANSPORT_ADDRESSSIG_TRANSPORT_EPHEMERALSIG_COMMUNICATOR_TCP_HANDSHAKESIG_COMMUNICATOR_TCP_REKEYSIG_COMMUNICATOR_UDP_HANDSHAKESIG_COMMUNICATOR_UDP_BROADCASTSIG_TRANSPORT_CHALLENGESIG_TRANSPORT_DV_HOPSIG_TRANSPORT_DV_INITIATORSIG_CADET_CONNECTION_INITIATORSIG_COMMUNICATOR_TCP_HANDSHAKE_ACK"
 )
 
 var (
        _SigPurpose_index_0 = [...]uint8{0, 8, 30, 54, 68, 95, 113, 124, 133}
        _SigPurpose_index_1 = [...]uint8{0, 14, 30, 46, 58, 77, 92, 105, 121}
        _SigPurpose_index_2 = [...]uint8{0, 21, 43, 65, 93}
-       _SigPurpose_index_3 = [...]uint16{0, 21, 33, 54, 77, 107, 133, 163, 
193, 216, 236, 262, 292}
+       _SigPurpose_index_3 = [...]uint16{0, 21, 33, 54, 77, 107, 133, 163, 
193, 216, 236, 262, 292, 326}
 )
 
 func (i SigPurpose) String() string {
@@ -66,7 +67,7 @@ func (i SigPurpose) String() string {
        case 20 <= i && i <= 23:
                i -= 20
                return 
_SigPurpose_name_2[_SigPurpose_index_2[i]:_SigPurpose_index_2[i+1]]
-       case 27 <= i && i <= 38:
+       case 27 <= i && i <= 39:
                i -= 27
                return 
_SigPurpose_name_3[_SigPurpose_index_3[i]:_SigPurpose_index_3[i+1]]
        default:
diff --git a/src/gnunet/go.mod b/src/gnunet/go.mod
index 6e0021b..4c12af3 100644
--- a/src/gnunet/go.mod
+++ b/src/gnunet/go.mod
@@ -1,6 +1,6 @@
 module gnunet
 
-go 1.18
+go 1.19
 
 require (
        github.com/bfix/gospel v1.2.21
diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go
index e3a2943..26d23c4 100644
--- a/src/gnunet/message/factory.go
+++ b/src/gnunet/message/factory.go
@@ -128,7 +128,7 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, 
error) {
        case enums.MSG_IDENTITY_START:
                return NewIdentityStartMsg(), nil
        case enums.MSG_IDENTITY_RESULT_CODE:
-               return NewIdentityResultCodeMsg(enums.RC_OK, ""), nil
+               return NewIdentityResultCodeMsg(0), nil
        case enums.MSG_IDENTITY_UPDATE:
                return NewIdentityUpdateMsg("", nil), nil
        case enums.MSG_IDENTITY_CREATE:
@@ -139,30 +139,38 @@ func NewEmptyMessage(msgType enums.MsgType) (Message, 
error) {
                return NewIdentityDeleteMsg(""), nil
        case enums.MSG_IDENTITY_LOOKUP:
                return NewIdentityLookupMsg(""), nil
-       case enums.MSG_IDENTITY_GET_DEFAULT:
-               return NewIdentityGetDefaultMsg(""), nil
-       case enums.MSG_IDENTITY_SET_DEFAULT:
-               return NewIdentitySetDefaultMsg(nil, ""), nil
 
        //------------------------------------------------------------------
        // Namestore service
        //------------------------------------------------------------------
 
        case enums.MSG_NAMESTORE_ZONE_ITERATION_START:
-               return NewNamestoreZoneIterStartMsg(nil), nil
+               return NewNamestoreZoneIterStartMsg(0, 0, nil), nil
        case enums.MSG_NAMESTORE_ZONE_ITERATION_NEXT:
+               return NewNamestoreZoneIterNextMsg(0, 0), nil
        case enums.MSG_NAMESTORE_ZONE_ITERATION_STOP:
+               return NewNamestoreZoneIterStopMsg(0), nil
+       case enums.MSG_NAMESTORE_ZONE_ITERATION_END:
+               return NewNamestoreZoneIterEndMsg(0), nil
        case enums.MSG_NAMESTORE_RECORD_STORE:
+               return NewNamestoreRecordStoreMsg(0, nil), nil
        case enums.MSG_NAMESTORE_RECORD_STORE_RESPONSE:
+               return NewNamestoreRecordStoreRespMsg(0, 0), nil
        case enums.MSG_NAMESTORE_RECORD_LOOKUP:
+               return NewNamestoreRecordLookupMsg(0, nil, "", false), nil
        case enums.MSG_NAMESTORE_RECORD_LOOKUP_RESPONSE:
+               return NewNamestoreRecordLookupRespMsg(0, nil, ""), nil
+       case enums.MSG_NAMESTORE_RECORD_RESULT:
+               return NewNamestoreRecordResultMsg(0, nil, ""), nil
        case enums.MSG_NAMESTORE_ZONE_TO_NAME:
+               return NewNamestoreZoneToNameMsg(0, nil, nil), nil
        case enums.MSG_NAMESTORE_ZONE_TO_NAME_RESPONSE:
+               return NewNamestoreZoneToNameRespMsg(0, nil, "", 0), nil
        case enums.MSG_NAMESTORE_MONITOR_START:
-       case enums.MSG_NAMESTORE_MONITOR_SYNC:
-       case enums.MSG_NAMESTORE_RECORD_RESULT:
-               return NewNamestoreRecordResultMsg(nil, ""), nil
+               return NewNamestoreMonitorStartMsg(0, nil, 0, 0), nil
        case enums.MSG_NAMESTORE_MONITOR_NEXT:
+               return NewNamestoreMonitorNextMsg(0, 0), nil
+       case enums.MSG_NAMESTORE_MONITOR_SYNC:
        }
        return nil, fmt.Errorf("unknown message type %d", msgType)
 }
diff --git a/src/gnunet/message/msg_identity.go 
b/src/gnunet/message/msg_identity.go
index fb8bba5..f22f328 100644
--- a/src/gnunet/message/msg_identity.go
+++ b/src/gnunet/message/msg_identity.go
@@ -62,35 +62,43 @@ func (msg *IdentityStartMsg) String() string {
 type IdentityUpdateMsg struct {
        MsgHeader
 
-       NameLen uint16              `order:"big"`
-       EOL     uint16              `order:"big"`
-       ZoneKey *crypto.ZonePrivate `init:"Init"`
-       Name_   []byte              `size:"NameLen"`
+       NameLen  uint16              `order:"big"`                // length of 
name
+       EOL      uint16              `order:"big"`                // flag for 
"end-of-list"
+       KeyLen   uint16              `order:"big"`                // length of 
key
+       Reserved uint16              `order:"big"`                // reserved
+       Name_    []byte              `size:"NameLen"`             // label name
+       ZoneKey  *crypto.ZonePrivate `init:"Init" opt:"(IsUsed)"` // zone key
 
        // transient state
        name string
 }
 
+// IsUsed to decide if key is used in message
+func (msg *IdentityUpdateMsg) IsUsed(fld string) bool {
+       return msg.EOL != uint16(enums.RC_YES)
+}
+
 // NewIdentityUpdateMsg creates an update message. If the zone key is nil,
 // a End-Of-List is triggered so the client knows we are done.
 func NewIdentityUpdateMsg(name string, zk *crypto.ZonePrivate) 
*IdentityUpdateMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       nl := uint16(len(name) + 1)
+       size := kl + nl + 12
        msg := &IdentityUpdateMsg{
-               MsgHeader: MsgHeader{8, enums.MSG_IDENTITY_UPDATE},
+               MsgHeader: MsgHeader{size, enums.MSG_IDENTITY_UPDATE},
+               name:      name,
+               Name_:     util.WriteCString(name),
+               NameLen:   nl,
+               KeyLen:    kl,
        }
        if zk == nil {
                // tag end-of-list
                msg.EOL = uint16(enums.RC_YES)
-               var size uint16
-               // assemble an empty zonekey
-               msg.ZoneKey, size = crypto.NullZonePrivate(enums.GNS_TYPE_PKEY)
-               msg.MsgSize += size
        } else {
-               msg.name = name
-               msg.Name_ = util.WriteCString(name)
-               msg.NameLen = uint16(len(msg.Name_))
-               msg.MsgSize += msg.NameLen
                msg.ZoneKey = zk
-               msg.MsgSize += uint16(zk.KeySize() + 4)
        }
        return msg
 }
@@ -124,37 +132,27 @@ func (msg *IdentityUpdateMsg) Name() string {
 type IdentityResultCodeMsg struct {
        MsgHeader
 
-       ResultCode enums.ResultCode `order:"big"`
-       Error      string           `opt:"(OnError)"`
-}
-
-// OnError returns true if an error message is attached
-func (msg *IdentityResultCodeMsg) OnError() bool {
-       return msg.ResultCode != enums.RC_OK
+       ResultCode uint32 `order:"big"`
 }
 
 // Init called after unmarshalling a message to setup internal state
 func (msg *IdentityResultCodeMsg) Init() error { return nil }
 
 // NewIdentityResultCodeMsg creates a new default message.
-func NewIdentityResultCodeMsg(rc enums.ResultCode, err string) 
*IdentityResultCodeMsg {
+func NewIdentityResultCodeMsg(rc int) *IdentityResultCodeMsg {
        msg := &IdentityResultCodeMsg{
                MsgHeader: MsgHeader{
                        MsgSize: 8,
                        MsgType: enums.MSG_IDENTITY_RESULT_CODE,
                },
-               ResultCode: rc,
-       }
-       if rc != enums.RC_OK {
-               msg.Error = err
-               msg.MsgSize += uint16(len(err) + 1)
+               ResultCode: uint32(rc),
        }
        return msg
 }
 
 // String returns a human-readable representation of the message.
 func (msg *IdentityResultCodeMsg) String() string {
-       return fmt.Sprintf("IdentityResultCodeMsg{rc=%d,err='%s'}", 
msg.ResultCode, msg.Error)
+       return fmt.Sprintf("IdentityResultCodeMsg{rc=%d}", msg.ResultCode)
 }
 
 //----------------------------------------------------------------------
@@ -167,10 +165,10 @@ func (msg *IdentityResultCodeMsg) String() string {
 type IdentityCreateMsg struct {
        MsgHeader
 
-       NameLen  uint16              `order:"big"`
-       Reserved uint16              `order:"big"`
-       ZoneKey  *crypto.ZonePrivate `init:"Init"`
-       Name_    []byte              `size:"NameLen"`
+       NameLen uint16              `order:"big"`    // length of label name
+       KeyLen  uint16              `order:"big"`    // length of key
+       ZoneKey *crypto.ZonePrivate `init:"Init"`    // zone key
+       Name_   []byte              `size:"NameLen"` // label name
 
        // transient state
        name string
@@ -361,113 +359,3 @@ func NewIdentityLookupMsg(name string) *IdentityLookupMsg 
{
 func (msg *IdentityLookupMsg) String() string {
        return fmt.Sprintf("IdentityLookupMsg{name='%s'}", msg.Name)
 }
-
-//----------------------------------------------------------------------
-// MSG_IDENTITY_GET_DEFAULT
-//
-// Get the default identity for named subsystem
-//----------------------------------------------------------------------
-
-// IdentityGetDefault to retrieve the default identity for a service
-type IdentityGetDefaultMsg struct {
-       MsgHeader
-
-       SrvLen   uint16 `order:"big"`
-       Reserved uint16 `order:"big"`
-       Service_ []byte `size:"SrvLen"`
-
-       // transient state
-       service string
-}
-
-// Init called after unmarshalling a message to setup internal state
-func (msg *IdentityGetDefaultMsg) Init() error {
-       msg.service, _ = util.ReadCString(msg.Service_, 0)
-       return nil
-}
-
-// NewIdentityGetDefaultMsg creates a new message
-func NewIdentityGetDefaultMsg(svc string) *IdentityGetDefaultMsg {
-       msg := &IdentityGetDefaultMsg{
-               MsgHeader: MsgHeader{
-                       MsgSize: 8,
-                       MsgType: enums.MSG_IDENTITY_DELETE,
-               },
-       }
-       if len(svc) > 0 {
-               msg.Service_ = util.WriteCString(svc)
-               msg.MsgSize += uint16(len(msg.Service_))
-               msg.service = svc
-       }
-       return msg
-}
-
-// String returns a human-readable representation of the message.
-func (msg *IdentityGetDefaultMsg) String() string {
-       return fmt.Sprintf("IdentityGetDefaultMsg{svc='%s'}", msg.service)
-}
-
-// Service name
-func (msg *IdentityGetDefaultMsg) Service() string {
-       return msg.service
-}
-
-//----------------------------------------------------------------------
-// MSG_IDENTITY_SET_DEFAULT
-//
-// Set default identity for named subsystem
-//----------------------------------------------------------------------
-
-// IdentitySetDefaultMsg sets a default identity (key) for a service
-type IdentitySetDefaultMsg struct {
-       MsgHeader
-
-       SrvLen   uint16              `order:"big"`
-       Reserved uint16              `order:"big"`
-       ZoneKey  *crypto.ZonePrivate `init:"Init"`
-       Service_ []byte              `size:"SrvLen"`
-
-       // transient state
-       service string
-}
-
-// Init called after unmarshalling a message to setup internal state
-func (msg *IdentitySetDefaultMsg) Init() error {
-       msg.service, _ = util.ReadCString(msg.Service_, 0)
-       return nil
-}
-
-// NewIdentitySetDefaultMsg renames an identity
-func NewIdentitySetDefaultMsg(zk *crypto.ZonePrivate, svc string) 
*IdentitySetDefaultMsg {
-       msg := &IdentitySetDefaultMsg{
-               MsgHeader: MsgHeader{
-                       MsgSize: 8,
-                       MsgType: enums.MSG_IDENTITY_DELETE,
-               },
-       }
-       if zk == nil {
-               // assemble an empty zonekey
-               var size uint16
-               msg.ZoneKey, size = crypto.NullZonePrivate(enums.GNS_TYPE_PKEY)
-               msg.MsgSize += size
-       } else {
-               msg.ZoneKey = zk
-               msg.MsgSize += uint16(zk.KeySize() + 4)
-       }
-       if len(svc) > 0 {
-               msg.Service_ = util.WriteCString(svc)
-               msg.MsgSize += uint16(len(msg.Service_))
-               msg.service = svc
-       }
-       return msg
-}
-
-// String returns a human-readable representation of the message.
-func (msg *IdentitySetDefaultMsg) String() string {
-       return fmt.Sprintf("IdentitySetDefaultMsg{key=%s,svc='%s'}", 
msg.ZoneKey.ID(), msg.service)
-}
-
-// Service name
-func (msg *IdentitySetDefaultMsg) Service() string {
-       return msg.service
-}
diff --git a/src/gnunet/message/msg_namestore.go 
b/src/gnunet/message/msg_namestore.go
index f03ef2c..9241e17 100644
--- a/src/gnunet/message/msg_namestore.go
+++ b/src/gnunet/message/msg_namestore.go
@@ -24,6 +24,8 @@ import (
        "gnunet/enums"
        "gnunet/service/dht/blocks"
        "gnunet/util"
+
+       "github.com/bfix/gospel/data"
 )
 
 //======================================================================
@@ -37,10 +39,10 @@ type GenericNamestoreMsg struct {
 }
 
 // return initialized common message header
-func newGenericNamestoreMsg(size uint16, mtype enums.MsgType) 
GenericNamestoreMsg {
+func newGenericNamestoreMsg(id uint32, size uint16, mtype enums.MsgType) 
GenericNamestoreMsg {
        return GenericNamestoreMsg{
                MsgHeader: MsgHeader{size, mtype},
-               ID:        uint32(util.NextID()),
+               ID:        id,
        }
 }
 
@@ -48,18 +50,28 @@ func newGenericNamestoreMsg(size uint16, mtype 
enums.MsgType) GenericNamestoreMs
 // MSG_NAMESTORE_ZONE_ITERATION_START
 //----------------------------------------------------------------------
 
-// NamestoreZoneIterStartMsg starts a new iteration over all zones
+// NamestoreZoneIterStartMsg starts a new iteration over all labels in a zones
 type NamestoreZoneIterStartMsg struct {
        GenericNamestoreMsg
 
+       Filter  uint16              `order:"big"` // filter settings
+       KeyLen  uint16              `order:"big"` // length of private key
        ZoneKey *crypto.ZonePrivate `init:"Init"` // private zone key
 }
 
 // NewNamecacheCacheMsg creates a new default message.
-func NewNamestoreZoneIterStartMsg(zone *crypto.ZonePrivate) 
*NamestoreZoneIterStartMsg {
+func NewNamestoreZoneIterStartMsg(id uint32, filter int, zone 
*crypto.ZonePrivate) *NamestoreZoneIterStartMsg {
+       var size uint16 = 16
+       var kl uint16 = 0
+       if zone != nil {
+               kl = uint16(zone.KeySize()) + 4
+               size += kl
+       }
        return &NamestoreZoneIterStartMsg{
-               GenericNamestoreMsg: newGenericNamestoreMsg(100, 
enums.MSG_NAMESTORE_ZONE_ITERATION_START),
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_ZONE_ITERATION_START),
+               Filter:              uint16(filter),
                ZoneKey:             zone,
+               KeyLen:              kl,
        }
 }
 
@@ -75,14 +87,19 @@ func (m *NamestoreZoneIterStartMsg) String() string {
 // MSG_NAMESTORE_ZONE_ITERATION_NEXT
 //----------------------------------------------------------------------
 
+// NamestoreZoneIterNextMsg returns the next labels
 type NamestoreZoneIterNextMsg struct {
        GenericNamestoreMsg
 
        Limit uint64 `order:"big"` // max. number of records in one go
 }
 
-func NewNamestoreZoneIterNextMsg() *NamestoreZoneIterNextMsg {
-       return &NamestoreZoneIterNextMsg{}
+// NewNamestoreZoneIterNextMsg creates a message with given limit
+func NewNamestoreZoneIterNextMsg(id uint32, limit int) 
*NamestoreZoneIterNextMsg {
+       return &NamestoreZoneIterNextMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 16, 
enums.MSG_NAMESTORE_ZONE_ITERATION_NEXT),
+               Limit:               uint64(limit),
+       }
 }
 
 // Init called after unmarshalling a message to setup internal state
@@ -97,132 +114,593 @@ func (m *NamestoreZoneIterNextMsg) String() string {
 // MSG_NAMESTORE_ZONE_ITERATION_STOP
 //----------------------------------------------------------------------
 
+// NamestoreZoneIterStopMsg stops a running iterator
 type NamestoreZoneIterStopMsg struct {
        GenericNamestoreMsg
 }
 
+// NewNamestoreZoneIterNextMsg creates a stop message
+func NewNamestoreZoneIterStopMsg(id uint32) *NamestoreZoneIterStopMsg {
+       return &NamestoreZoneIterStopMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 8, 
enums.MSG_NAMESTORE_ZONE_ITERATION_STOP),
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreZoneIterStopMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreZoneIterStopMsg) String() string {
+       return fmt.Sprintf("NamestoreZoneIterStopMsg{id=%d}", m.ID)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_ZONE_ITERATION_END
+//----------------------------------------------------------------------
+
+// NamestoreZoneIterEndMsg stops a running iterator
+type NamestoreZoneIterEndMsg struct {
+       GenericNamestoreMsg
+}
+
+// NewNamestoreZoneIterEndMsg creates a stop message
+func NewNamestoreZoneIterEndMsg(id uint32) *NamestoreZoneIterEndMsg {
+       return &NamestoreZoneIterEndMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 8, 
enums.MSG_NAMESTORE_ZONE_ITERATION_END),
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreZoneIterEndMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreZoneIterEndMsg) String() string {
+       return fmt.Sprintf("NamestoreZoneIterEndMsg{id=%d}", m.ID)
+}
+
 //----------------------------------------------------------------------
 // MSG_NAMESTORE_RECORD_RESULT
 //----------------------------------------------------------------------
 
+// NamestoreRecordResultMsg returns the records for a label (name)
 type NamestoreRecordResultMsg struct {
        GenericNamestoreMsg
 
-       Expire   util.AbsoluteTime   ``               // expiration date
-       NameLen  uint16              `order:"big"`    // length of name
-       RdLen    uint16              `order:"big"`    // size of record data
-       RdCount  uint16              `order:"big"`    // number of records
-       Reserved uint16              `order:"big"`    // alignment
-       ZoneKey  *crypto.ZonePrivate `init:"Init"`    // private zone key
-       Name     []byte              `size:"NameLen"` // name string
-       Records  []byte              `size:"RdLen"`   // serialized record data
+       Expire  util.AbsoluteTime   ``               // expiration date
+       NameLen uint16              `order:"big"`    // length of name
+       RdLen   uint16              `order:"big"`    // size of record data
+       RdCount uint16              `order:"big"`    // number of records
+       KeyLen  uint16              `order:"big"`    // length of key
+       ZoneKey *crypto.ZonePrivate `init:"Init"`    // private zone key
+       Name    []byte              `size:"NameLen"` // name string
+       Records []byte              `size:"RdLen"`   // serialized record data
+
+       // transient state
+       recset *blocks.RecordSet
 }
 
-func NewNamestoreRecordResultMsg(zk *crypto.ZonePrivate, label string) 
*NamestoreRecordResultMsg {
+// NewNamestoreRecordResultMsg returns an initialize record message
+func NewNamestoreRecordResultMsg(id uint32, zk *crypto.ZonePrivate, label 
string) *NamestoreRecordResultMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize()) + 4
+       }
+       nl := uint16(len(label) + 1)
+       size := kl + nl + 24
        return &NamestoreRecordResultMsg{
-               Expire:  util.AbsoluteTimeNever(),
-               ZoneKey: zk,
-               NameLen: uint16(len(label)),
-               Name:    []byte(label),
-               RdLen:   0,
-               RdCount: 0,
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_RECORD_RESULT),
+               Expire:              util.AbsoluteTimeNever(),
+               KeyLen:              kl,
+               ZoneKey:             zk,
+               NameLen:             nl,
+               Name:                util.WriteCString(label),
+               RdLen:               0,
+               RdCount:             0,
        }
 }
 
 // Init called after unmarshalling a message to setup internal state
-func (m *NamestoreRecordResultMsg) Init() error { return nil }
+func (m *NamestoreRecordResultMsg) Init() error {
+       if m.recset == nil {
+               m.recset = new(blocks.RecordSet)
+               return data.Unmarshal(m.recset, m.Records)
+       }
+       return nil
+}
+
+// AddRecords adds the record data to the message
+func (m *NamestoreRecordResultMsg) AddRecords(rs *blocks.RecordSet) {
+       // make sure the record set is padded correctly
+       rs.SetPadding()
+       // copy recordset to message
+       m.RdCount = uint16(rs.Count)
+       m.Records = rs.RDATA()
+       m.RdLen = uint16(len(m.Records))
+       m.MsgSize += m.RdLen
+       m.recset = rs
+}
+
+// GetRecords returns the record set contained in message
+func (m *NamestoreRecordResultMsg) GetRecords() blocks.RecordSet {
+       return *m.recset
+}
 
 // String returns a human-readable representation of the message.
 func (m *NamestoreRecordResultMsg) String() string {
-       return 
fmt.Sprintf("NamestoreRecordResultMsg{id=%d,zone=%s,label='%s'}", m.ID, 
m.ZoneKey.ID(), string(m.Name))
+       zone, label := "", ""
+       if !m.ZoneKey.IsNull() {
+               zone = fmt.Sprintf(",zone=%s", m.ZoneKey.ID())
+       }
+       if m.NameLen > 0 {
+               lbl, _ := util.ReadCString(m.Name, 0)
+               label = fmt.Sprintf(",label='%s'", lbl)
+       }
+       return fmt.Sprintf("NamestoreRecordResultMsg{id=%d%s%s,%d records}",
+               m.ID, zone, label, m.RdCount)
 }
 
 //----------------------------------------------------------------------
+// MSG_NAMESTORE_RECORD_STORE
 //----------------------------------------------------------------------
 
+// NamestoreRecordSet for a label
+type NamestoreRecordSet struct {
+       NameLen  uint16 `order:"big"`    // Length of label
+       RdLen    uint16 `order:"big"`    // length of record data
+       RdCount  uint16 `order:"big"`    // number of records
+       Reserved uint16 `order:"big"`    // reserved
+       Name     []byte `size:"NameLen"` // label name
+       RecData  []byte `size:"RdLen"`   // record data
+}
+
+// NewNamestoreRecordSet for label and resource records.
+func NewNamestoreRecordSet(label string, rr *blocks.RecordSet) (rs 
*NamestoreRecordSet, size uint16) {
+       // make sure the record set is padded correctly
+       rr.SetPadding()
+
+       // copy recordset to message
+       rs = new(NamestoreRecordSet)
+       rs.NameLen = uint16(len(label) + 1)
+       rs.Name = util.WriteCString(label)
+       rs.RdCount = uint16(rr.Count)
+       rs.RecData = rr.RDATA()
+       rs.RdLen = uint16(len(rs.RecData))
+       size = rs.RdLen + rs.NameLen + 8
+       return
+}
+
+//----------------------------------------------------------------------
+
+// NamestoreRecordStoreMsg for storing records (multiple labels at a
+// time possible)
 type NamestoreRecordStoreMsg struct {
        GenericNamestoreMsg
 
-       ZoneKey *crypto.ZonePrivate // private zone key
-       Records *blocks.RecordSet   // list of records
+       Count   uint16                `order:"big"`  // number of RecordSets
+       KeyLen  uint16                `order:"big"`  // length of zone key
+       ZoneKey *crypto.ZonePrivate   `init:"Init"`  // private zone key
+       RSets   []*NamestoreRecordSet `size:"Count"` // list of label record 
sets
+}
+
+// NewNamestoreRecordStoreMsg creates an initialized message (without records)
+func NewNamestoreRecordStoreMsg(id uint32, zk *crypto.ZonePrivate) 
*NamestoreRecordStoreMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       size := kl + 14
+       return &NamestoreRecordStoreMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_RECORD_STORE),
+               ZoneKey:             zk,
+               Count:               0,
+               KeyLen:              kl,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreRecordStoreMsg) Init() error {
+       return nil
+}
+
+// AddRecords adds the record data to the message
+func (m *NamestoreRecordStoreMsg) AddRecordSet(label string, rr 
*blocks.RecordSet) {
+       rs, size := NewNamestoreRecordSet(label, rr)
+       m.RSets = append(m.RSets, rs)
+       m.MsgSize += size
+}
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreRecordStoreMsg) String() string {
+       return fmt.Sprintf("NamestoreRecordStoreMsg{id=%d,zone=%s,%d record 
sets}",
+               m.ID, m.ZoneKey.ID(), m.Count)
 }
 
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_RECORD_STORE_RESP
+//----------------------------------------------------------------------
+
+// NamestoreRecordStoreRespMsg is a response to a record store message
 type NamestoreRecordStoreRespMsg struct {
        GenericNamestoreMsg
 
-       Status   int32  `order:"big"`   // result status
-       ErrLen   uint16 `order:"big"`   // length of error message
-       Reserved uint16 `order:"big"`   // alignment
-       Error    []byte `size:"ErrLen"` // error message
+       Status uint32 `order:"big"` // result status
+}
+
+// NewNamestoreRecordStoreRespMsg creates a new message
+func NewNamestoreRecordStoreRespMsg(id uint32, rc uint32) 
*NamestoreRecordStoreRespMsg {
+       return &NamestoreRecordStoreRespMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 12, 
enums.MSG_NAMESTORE_RECORD_STORE_RESPONSE),
+               Status:              rc,
+       }
 }
 
-type NamestoreLabelLookupMsg struct {
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreRecordStoreRespMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreRecordStoreRespMsg) String() string {
+       return fmt.Sprintf("NamestoreRecordStoreRespMsg{id=%d,rc=%d}", m.ID, 
m.Status)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_RECORD_LOOKUP
+//----------------------------------------------------------------------
+
+// NamestoreRecordLookupMsg looks up a record in the namestore
+type NamestoreRecordLookupMsg struct {
        GenericNamestoreMsg
 
-       LblLen  uint32              `order:"big"` // length of label
-       IsEdit  uint32              `order:"big"` // lookup corresponds to edit 
request
-       ZoneKey *crypto.ZonePrivate // private zone key
+       LblLen  uint16              `order:"big"`   // length of label
+       IsEdit  uint16              `order:"big"`   // lookup corresponds to 
edit request
+       Filter  uint16              `order:"big"`   // filter flags
+       KeyLen  uint16              `order:"big"`   // size of key
+       ZoneKey *crypto.ZonePrivate `init:"Init"`   // private zone key
        Label   []byte              `size:"LblLen"` // label string
 }
 
-type NamestoreLabelLookupRespMsg struct {
+// NewNamestoreRecordLookupMsg creates a new message
+func NewNamestoreRecordLookupMsg(id uint32, zk *crypto.ZonePrivate, label 
string, isEdit bool) *NamestoreRecordLookupMsg {
+       var flag uint16
+       if isEdit {
+               flag = 1
+       }
+       var kl uint16
+       if zk != nil {
+               kl += uint16(zk.KeySize() + 4)
+       }
+       size := kl + uint16(len(label)) + 16
+       return &NamestoreRecordLookupMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_RECORD_LOOKUP),
+               IsEdit:              flag,
+               KeyLen:              kl,
+               ZoneKey:             zk,
+               LblLen:              uint16(len(label)),
+               Label:               []byte(label),
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreRecordLookupMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreRecordLookupMsg) String() string {
+       return 
fmt.Sprintf("NamestoreRecordLookupMsg{id=%d,zk=%s,label=%s,edit=%v}",
+               m.ID, m.ZoneKey.ID(), string(m.Label), m.IsEdit != 0)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_RECORD_LOOKUP_RESPONSE
+//----------------------------------------------------------------------
+
+// NamestoreRecordLookupRespMsg is a lookup response message
+type NamestoreRecordLookupRespMsg struct {
        GenericNamestoreMsg
 
-       LblLen  uint16              `order:"big"` // Length of label
-       RdLen   uint16              `order:"big"` // size of record data
-       RdCount uint16              `order:"big"` // number of records
-       Found   int16               `order:"big"` // label found?
-       ZoneKey *crypto.ZonePrivate // private zone key
-       Label   []byte              `size:"LblLen"` // label string
-       Records []byte              `size:"RdLen"`  // serialized record data
+       LblLen   uint16              `order:"big"`   // Length of label
+       RdLen    uint16              `order:"big"`   // size of record data
+       RdCount  uint16              `order:"big"`   // number of records
+       Found    int16               `order:"big"`   // label found?
+       Reserved uint16              `order:"big"`   // reserved
+       KeyLen   uint16              `order:"big"`   // length of key
+       ZoneKey  *crypto.ZonePrivate `init:"Init"`   // private zone key
+       Label    []byte              `size:"LblLen"` // label string
+       Records  []byte              `size:"RdLen"`  // serialized record data
+
+       // transient state
+       recset *blocks.RecordSet
 }
 
+// NewNamestoreRecordLookupRespMsg creates a new message
+func NewNamestoreRecordLookupRespMsg(id uint32, zk *crypto.ZonePrivate, label 
string) *NamestoreRecordLookupRespMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       size := kl + uint16(len(label)) + 20
+       msg := &NamestoreRecordLookupRespMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_RECORD_LOOKUP_RESPONSE),
+               KeyLen:              kl,
+               ZoneKey:             zk,
+               LblLen:              uint16(len(label)),
+               Label:               []byte(label),
+               Records:             nil,
+       }
+       return msg
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreRecordLookupRespMsg) Init() error {
+       if m.recset == nil {
+               m.recset = new(blocks.RecordSet)
+               return data.Unmarshal(m.recset, m.Records)
+       }
+       return nil
+}
+
+// AddRecords adds the record data to the message
+func (m *NamestoreRecordLookupRespMsg) AddRecords(rs *blocks.RecordSet) {
+       // make sure the record set is padded correctly
+       rs.SetPadding()
+       // copy recordset to message
+       m.RdCount = uint16(rs.Count)
+       m.Records = rs.RDATA()
+       m.RdLen = uint16(len(m.Records))
+       m.MsgSize += m.RdLen
+       m.recset = rs
+}
+
+// GetRecords returns the record set contained in message
+func (m *NamestoreRecordLookupRespMsg) GetRecords() blocks.RecordSet {
+       return *m.recset
+}
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreRecordLookupRespMsg) String() string {
+       return fmt.Sprintf("NamestoreRecordlLookupRespMsg{id=%d,found=%v,%d 
records}",
+               m.ID, m.Found == int16(enums.RC_YES), m.RdCount)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_ZONE_TO_NAME
+//----------------------------------------------------------------------
+
+// NamestoreZoneToNameMsg resolves the name for a given key
 type NamestoreZoneToNameMsg struct {
        GenericNamestoreMsg
 
-       ZoneKey    *crypto.ZonePrivate // private zone key
-       ZonePublic *crypto.ZoneKey     // public zone key
+       KeyLen     uint16              `order:"big"` // length of private key
+       PubLen     uint16              `order:"big"` // length of public key
+       ZoneKey    *crypto.ZonePrivate `init:"Init"` // private zone key
+       ZonePublic *crypto.ZoneKey     `init:"Init"` // public derived zone key
 }
 
+// NewNamestoreZoneIterNextMsg creates a new message
+func NewNamestoreZoneToNameMsg(id uint32, zk *crypto.ZonePrivate, pk 
*crypto.ZoneKey) *NamestoreZoneToNameMsg {
+       var kl, pl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       if pk != nil {
+               pl = uint16(pk.KeySize() + 4)
+       }
+       size := kl + pl + 12
+       msg := &NamestoreZoneToNameMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_ZONE_TO_NAME),
+               KeyLen:              kl,
+               PubLen:              pl,
+               ZoneKey:             zk,
+               ZonePublic:          pk,
+       }
+       return msg
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreZoneToNameMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreZoneToNameMsg) String() string {
+       var key string
+       if m.ZoneKey == nil {
+               key = "sec:" + m.ZonePublic.ID()
+       } else {
+               key = "pub:" + m.ZoneKey.Public().ID()
+       }
+       return fmt.Sprintf("NamestoreZoneToNameMsg{id=%d,zk=%s}", m.ID, key)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_ZONE_TO_NAME_RESPONSE
+//----------------------------------------------------------------------
+
+// NamestoreZoneToNameRespMsg is a response to NamestoreZoneToNameMsg
 type NamestoreZoneToNameRespMsg struct {
        GenericNamestoreMsg
 
-       NameLen uint16              `order:"big"` // length of name
-       RdLen   uint16              `order:"big"` // size of record data
-       RdCount uint16              `order:"big"` // number of records
-       Status  int16               `order:"big"` // result status
-       ZoneKey *crypto.ZonePrivate // private zone key
+       Status  enums.ErrorCode     `order:"big"`    // result status (error 
code)
+       NameLen uint16              `order:"big"`    // length of name
+       RdLen   uint16              `order:"big"`    // size of record data
+       RdCount uint16              `order:"big"`    // number of records
+       KeyLen  uint16              `order:"big"`    // length of key
+       ZoneKey *crypto.ZonePrivate `init:"Init"`    // private zone key
        Name    []byte              `size:"NameLen"` // name string
        Records []byte              `size:"RdLen"`   // serialized record data
+
+       // transient state
+       recset *blocks.RecordSet
+}
+
+// NewNamestoreNamestoreZoneToNameRespMsgMsg creates a new message
+func NewNamestoreZoneToNameRespMsg(id uint32, zk *crypto.ZonePrivate, label 
string, status enums.ErrorCode) *NamestoreZoneToNameRespMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       nl := uint16(len(label) + 1)
+       size := kl + nl + 12
+       return &NamestoreZoneToNameRespMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_ZONE_TO_NAME_RESPONSE),
+               Status:              status,
+               NameLen:             nl,
+               Name:                util.WriteCString(label),
+               KeyLen:              kl,
+               ZoneKey:             zk,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreZoneToNameRespMsg) Init() error {
+       if m.recset == nil {
+               m.recset = new(blocks.RecordSet)
+               return data.Unmarshal(m.recset, m.Records)
+       }
+       return nil
 }
 
+// AddRecords adds the record data to the message
+func (m *NamestoreZoneToNameRespMsg) AddRecords(rs *blocks.RecordSet) {
+       // make sure the record set is padded correctly
+       rs.SetPadding()
+       // copy recordset to message
+       m.RdCount = uint16(rs.Count)
+       m.Records = rs.RDATA()
+       m.RdLen = uint16(len(m.Records))
+       m.MsgSize += m.RdLen
+       m.recset = rs
+}
+
+// GetRecords returns the record set contained in message
+func (m *NamestoreZoneToNameRespMsg) GetRecords() blocks.RecordSet {
+       return *m.recset
+}
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreZoneToNameRespMsg) String() string {
+       return 
fmt.Sprintf("NamestoreZoneToNameRespMsg{id=%d,zone=%s,label='%s',%d records}",
+               m.ID, m.ZoneKey.ID(), string(m.Name), m.RdCount)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_TX_CONTROL
+//----------------------------------------------------------------------
+
+// NamestoreTxControlMsg to initiate a Tx control
 type NamestoreTxControlMsg struct {
        GenericNamestoreMsg
 
-       Control  uint16 `order:"big"` // type of control message
-       Reserved uint16 `order:"big"` // alignment
+       Reserved uint16 `order:"big"` // reserved
+       Control  uint16 `order:"big"` // type of control message to send
 }
 
+// NewNamestoreTxControlMsg creates a new message
+func NewNamestoreTxControlMsg(id uint32, ctrl uint16) *NamestoreTxControlMsg {
+       return &NamestoreTxControlMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 12, 
enums.MSG_NAMESTORE_TX_CONTROL),
+               Control:             ctrl,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreTxControlMsg) Init() error {
+       return nil
+}
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreTxControlMsg) String() string {
+       return fmt.Sprintf("NamestoreTxControlMsg{id=%d,ctrl=%d}", m.ID, 
m.Control)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_TX_CONTROL_RESULT
+//----------------------------------------------------------------------
+
+// NamestoreTxControlResultMsg is a response to a Tx control message
 type NamestoreTxControlResultMsg struct {
        GenericNamestoreMsg
 
-       Control uint16 `order:"big"` // type of control message
-       Status  uint16 `order:"big"` // result status
-       Error   []byte `size:"*"`    // error message (on status != OK)
+       Result enums.ErrorCode `order:"big"` // error code
 }
 
-type NamestoreZoneMonStartMsg struct {
+// NewNamestoreTxControlResultMsg creates a new message
+func NewNamestoreTxControlResultMsg(id uint32, ec enums.ErrorCode) 
*NamestoreTxControlResultMsg {
+       return &NamestoreTxControlResultMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 12, 
enums.MSG_NAMESTORE_TX_CONTROL_RESULT),
+               Result:              ec,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreTxControlResultMsg) Init() error {
+       return nil
+}
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreTxControlResultMsg) String() string {
+       return fmt.Sprintf("NamestoreTxControlResultMsg{id=%d,result=%s}", 
m.ID, m.Result)
+}
+
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_MONITOR_START
+//----------------------------------------------------------------------
+
+// NamestoreMonitorStartMsg starts a monitor session
+type NamestoreMonitorStartMsg struct {
        GenericNamestoreMsg
 
-       Iterate  uint32              `order:"big"` // iterate over all records
-       Filter   uint16              `order:"big"` // filter flags
-       Reserved uint16              `order:"big"` // alignment
-       ZoneKey  *crypto.ZonePrivate // private zone key
+       Iterate enums.ResultCode    `order:"big"` // iterate over all records
+       Filter  uint16              `order:"big"` // filter flags
+       KeyLen  uint16              `order:"big"` // length of key
+       ZoneKey *crypto.ZonePrivate `init:"Init"` // private zone key
+}
+
+// NewNamestoreMonitorStartMsg creates a new message
+func NewNamestoreMonitorStartMsg(id uint32, zk *crypto.ZonePrivate, iter 
enums.ResultCode, filter int) *NamestoreMonitorStartMsg {
+       var kl uint16
+       if zk != nil {
+               kl = uint16(zk.KeySize() + 4)
+       }
+       size := kl + 16
+       return &NamestoreMonitorStartMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, size, 
enums.MSG_NAMESTORE_MONITOR_START),
+               Iterate:             iter,
+               Filter:              uint16(filter),
+               KeyLen:              kl,
+               ZoneKey:             zk,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreMonitorStartMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreMonitorStartMsg) String() string {
+       return 
fmt.Sprintf("NamestoreMonitorStartMsg{id=%d,zone=%s,iter=%v,filter=%d}",
+               m.ID, m.ZoneKey.ID(), m.Iterate == enums.RC_OK, m.Filter)
 }
 
-type NamestoreZoneMonNextMsg struct {
+//----------------------------------------------------------------------
+// MSG_NAMESTORE_MONITOR_NEXT
+//----------------------------------------------------------------------
+
+// NamestoreMonitorNextMsg to retrieve next set of results
+type NamestoreMonitorNextMsg struct {
        GenericNamestoreMsg
 
        Reserved uint32 `order:"big"` // alignment =0
        Limit    uint64 `order:"big"` // max. number of records in one go
 }
+
+// NewNamestoreMonitorNextMsg creates a new message
+func NewNamestoreMonitorNextMsg(id uint32, limit uint64) 
*NamestoreMonitorNextMsg {
+       return &NamestoreMonitorNextMsg{
+               GenericNamestoreMsg: newGenericNamestoreMsg(id, 20, 
enums.MSG_NAMESTORE_MONITOR_NEXT),
+               Limit:               limit,
+       }
+}
+
+// Init called after unmarshalling a message to setup internal state
+func (m *NamestoreMonitorNextMsg) Init() error { return nil }
+
+// String returns a human-readable representation of the message.
+func (m *NamestoreMonitorNextMsg) String() string {
+       return fmt.Sprintf("NamestoreMonitorNextMsg{id=%d,limit=%d}", m.ID, 
m.Limit)
+}
diff --git a/src/gnunet/service/dht/blocks/gns.go 
b/src/gnunet/service/dht/blocks/gns.go
index b504bc5..e419279 100644
--- a/src/gnunet/service/dht/blocks/gns.go
+++ b/src/gnunet/service/dht/blocks/gns.go
@@ -19,6 +19,8 @@
 package blocks
 
 import (
+       "bytes"
+       "encoding/binary"
        "errors"
        "fmt"
        "gnunet/crypto"
@@ -221,7 +223,11 @@ func (b *GNSBlock) Verify() (ok bool, err error) {
        return b.DerivedKeySig.Verify(buf)
 }
 
-// RecordSet ist the GNUnet data structure for a list of resource records
+//----------------------------------------------------------------------
+// Resource record
+//----------------------------------------------------------------------
+
+// RecordSet is the GNUnet data structure for a list of resource records
 // in a GNSBlock. As part of GNUnet messages, the record set is padded so that
 // the binary size of (records||padding) is the smallest power of two.
 type RecordSet struct {
@@ -239,6 +245,36 @@ func NewRecordSet() *RecordSet {
        }
 }
 
+// NewRecordSetFromRDATA converts RDATA (see GNS spec) to rcord set
+func NewRecordSetFromRDATA(count uint32, rdata []byte) (rs *RecordSet, err 
error) {
+       rs = new(RecordSet)
+
+       // do we know the number of records?
+       if count == 0 {
+               // no: try to compute from rdata
+               var size uint16
+               for pos := 8; pos < len(rdata); {
+                       if err = binary.Read(bytes.NewReader(rdata[pos:pos+2]), 
binary.BigEndian, &size); err != nil {
+                               err = nil
+                               break
+                       }
+                       count++
+                       pos += int(size) + 16
+               }
+       }
+       if count == 0 {
+               return
+       }
+       // generate intermediate buffer
+       wrt := new(bytes.Buffer)
+       _ = binary.Write(wrt, binary.BigEndian, count)
+       _, _ = wrt.Write(rdata)
+       buf := wrt.Bytes()
+       // unmarshal record set
+       err = data.Unmarshal(rs, buf)
+       return
+}
+
 // AddRecord to append a resource record to the set.
 func (rs *RecordSet) AddRecord(rec *ResourceRecord) {
        rs.Count++
@@ -249,7 +285,7 @@ func (rs *RecordSet) AddRecord(rec *ResourceRecord) {
 func (rs *RecordSet) SetPadding() {
        size := 0
        for _, rr := range rs.Records {
-               size += int(rr.Size) + 20
+               size += int(rr.Size) + 16
        }
        n := 1
        for n < size {
@@ -271,8 +307,9 @@ func (rs *RecordSet) Expire() util.AbsoluteTime {
        return expires
 }
 
-// Bytes returns the binary representation
-func (rs *RecordSet) Bytes() []byte {
+// RDATA returns the binary representation of the record set as specified
+// in the GNS draft.
+func (rs *RecordSet) RDATA() []byte {
        // make sure padding exists
        if rs.Padding == nil {
                rs.SetPadding()
@@ -282,16 +319,20 @@ func (rs *RecordSet) Bytes() []byte {
        if err != nil {
                return nil
        }
-       return buf
+       return buf[4:]
 }
 
+//----------------------------------------------------------------------
+// Resource record
+//----------------------------------------------------------------------
+
 // ResourceRecord is the GNUnet-specific representation of resource
 // records (not to be confused with DNS resource records).
 type ResourceRecord struct {
-       Expire util.AbsoluteTime // Expiration time for the record
-       Size   uint32            `order:"big"` // Number of bytes in 'Data'
+       Expire util.AbsoluteTime ``            // Expiration time for the record
+       Size   uint16            `order:"big"` // Number of bytes in 'Data'
+       Flags  enums.GNSFlag     `order:"big"` // Flags
        RType  enums.GNSType     `order:"big"` // Type of the GNS/DNS record
-       Flags  enums.GNSFlag     `order:"big"` // Flags for the record
        Data   []byte            `size:"Size"` // Record data
 }
 
diff --git a/src/gnunet/service/gns/block_handler.go 
b/src/gnunet/service/gns/block_handler.go
index b00fac2..435b9de 100644
--- a/src/gnunet/service/gns/block_handler.go
+++ b/src/gnunet/service/gns/block_handler.go
@@ -151,7 +151,7 @@ func NewBlockHandlerList(records []*blocks.ResourceRecord, 
labels []string) (*Bl
        // Third pass: Traverse active list and build list of handler instances.
        for _, rec := range active {
                // update counter map for non-supplemental records
-               if (rec.Flags & enums.GNS_FLAG_SUPPL) != 0 {
+               if (rec.Flags & enums.GNS_FLAG_SUPPLEMENTAL) != 0 {
                        logger.Printf(logger.DBG, "[gns] handler_list: skip 
%v\n", rec)
                        continue
                }
@@ -434,7 +434,7 @@ func (h *BoxHandler) Records(kind RRTypeList) 
*blocks.RecordSet {
                        rr.Expire = box.rec.Expire
                        rr.Flags = box.rec.Flags
                        rr.RType = box.Type
-                       rr.Size = uint32(len(box.RR))
+                       rr.Size = uint16(len(box.RR))
                        rr.Data = box.RR
                        rs.AddRecord(rr)
                }
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go
index 32c71e9..c2f62c6 100644
--- a/src/gnunet/service/gns/dns.go
+++ b/src/gnunet/service/gns/dns.go
@@ -179,7 +179,7 @@ func QueryDNS(id int, name string, server net.IP, kind 
RRTypeList) *blocks.Recor
                                rr.Expire = util.NewAbsoluteTime(expires)
                                rr.Flags = 0
                                rr.RType = enums.GNSType(record.Header().Rrtype)
-                               rr.Size = uint32(record.Header().Rdlength)
+                               rr.Size = record.Header().Rdlength
                                rr.Data = make([]byte, rr.Size)
 
                                if n < int(rr.Size) {
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
index 37bbfc5..6078674 100644
--- a/src/gnunet/service/gns/module.go
+++ b/src/gnunet/service/gns/module.go
@@ -32,7 +32,6 @@ import (
        "gnunet/service/revocation"
        "gnunet/util"
 
-       "github.com/bfix/gospel/data"
        "github.com/bfix/gospel/logger"
 )
 
@@ -365,7 +364,7 @@ func (m *Module) ResolveRelative(
        // asking for explicitly.
        if set.Count > 0 {
                for _, rec := range records {
-                       if !kind.HasType(rec.RType) && 
(rec.Flags&enums.GNS_FLAG_SUPPL) != 0 {
+                       if !kind.HasType(rec.RType) && 
(rec.Flags&enums.GNS_FLAG_SUPPLEMENTAL) != 0 {
                                set.AddRecord(rec)
                        }
                }
@@ -475,9 +474,9 @@ func (m *Module) Lookup(
 func (m *Module) newLEHORecord(name string, expires util.AbsoluteTime) 
*blocks.ResourceRecord {
        rr := new(blocks.ResourceRecord)
        rr.Expire = expires
-       rr.Flags = enums.GNS_FLAG_SUPPL
+       rr.Flags = enums.GNS_FLAG_SUPPLEMENTAL
        rr.RType = enums.GNS_TYPE_LEHO
-       rr.Size = uint32(len(name) + 1)
+       rr.Size = uint16(len(name) + 1)
        rr.Data = make([]byte, rr.Size)
        copy(rr.Data, []byte(name))
        rr.Data[len(name)] = 0
@@ -486,9 +485,9 @@ func (m *Module) newLEHORecord(name string, expires 
util.AbsoluteTime) *blocks.R
 
 // Records returns the list of resource records from binary data.
 func (m *Module) records(buf []byte) ([]*blocks.ResourceRecord, error) {
-       // parse  data into record set
-       rs := blocks.NewRecordSet()
-       if err := data.Unmarshal(rs, buf); err != nil {
+       // parse data into record set
+       rs, err := blocks.NewRecordSetFromRDATA(0, buf)
+       if err != nil {
                return nil, err
        }
        return rs.Records, nil
diff --git a/src/gnunet/service/gns/rr/gns.go b/src/gnunet/service/gns/rr/gns.go
index 1926a4a..b21f75c 100644
--- a/src/gnunet/service/gns/rr/gns.go
+++ b/src/gnunet/service/gns/rr/gns.go
@@ -111,7 +111,7 @@ func (rr *REDIRECT) Coexist(list []*enums.GNSSpec, label 
string) (ok bool, force
        }
        // make sure all existing records are supplemental EDKEYs too
        for _, e := range list {
-               if e.Type != enums.GNS_TYPE_REDIRECT && 
e.Flags&enums.GNS_FLAG_SUPPL == 0 {
+               if e.Type != enums.GNS_TYPE_REDIRECT && 
e.Flags&enums.GNS_FLAG_SUPPLEMENTAL == 0 {
                        // check failed on non-supplemental non-REDIRECT record
                        return
                }
diff --git a/src/gnunet/service/store/store_zonemaster.go 
b/src/gnunet/service/store/store_zonemaster.go
index 1338c01..17c5b81 100644
--- a/src/gnunet/service/store/store_zonemaster.go
+++ b/src/gnunet/service/store/store_zonemaster.go
@@ -32,12 +32,8 @@ import (
 )
 
 //============================================================
-// Local identities and zone records (SQLite3 database)
-// Identities are named ZonePrivate keys that are associated
-// with a GNUnet subsystem (like GNS, CADET and others).
-// Identities for the subsystem "gns" are called zones and
-// are collections of labeled resource record sets. All other
-// identities are usuall called "egos".
+// Zones are named ZonePrivate keys that act as a container
+// for labeled resource record sets in GNS.
 //============================================================
 
 // Zone is the definition of a local GNS zone
@@ -63,31 +59,18 @@ func NewZone(name string, sk *crypto.ZonePrivate) *Zone {
 
 //----------------------------------------------------------------------
 
-// Identity is a Zone associated with a service
-type Identity struct {
-       Zone
-
-       Svc string // associated service
-}
-
-// NewIdentity creates an initialize instance for database access
-func NewIdentity(name string, sk *crypto.ZonePrivate, svc string) *Identity {
-       return &Identity{
-               Zone: *NewZone(name, sk),
-               Svc:  svc,
-       }
-}
-
-//----------------------------------------------------------------------
-
+// Label is a named container for resource records in a GNS zone.
 type Label struct {
        ID       int64             // database id of label
        Zone     int64             // database ID of parent zone
        Name     string            // label name
        Created  util.AbsoluteTime // date of creation
        Modified util.AbsoluteTime // date of last modification
+       KeyHash  *crypto.HashCode  // hashcode of the label under zone
 }
 
+// NewLabel returns a new label with given name. It is not
+// associated with a zone yet.
 func NewLabel(label string) *Label {
        lbl := new(Label)
        lbl.ID = 0
@@ -98,6 +81,17 @@ func NewLabel(label string) *Label {
        return lbl
 }
 
+// SetZone links a label with a zone
+func (l *Label) SetZone(z *Zone) error {
+       pk, _, err := z.Key.Public().Derive(l.Name, "gns")
+       if err != nil {
+               return err
+       }
+       l.Zone = z.ID
+       l.KeyHash = crypto.Hash(pk.KeyData)
+       return nil
+}
+
 //----------------------------------------------------------------------
 
 // Record for GNS resource in a zone (generic). It is the responsibility
@@ -121,7 +115,7 @@ func NewRecord(expire util.AbsoluteTime, rtype 
enums.GNSType, flags enums.GNSFla
        rec.RType = rtype
        rec.Flags = flags
        rec.Data = data
-       rec.Size = uint32(len(rec.Data))
+       rec.Size = uint16(len(rec.Data))
        rec.Created = util.AbsoluteTimeNow()
        rec.Modified = util.AbsoluteTimeNow()
        return rec
@@ -157,7 +151,7 @@ func OpenZoneDB(fname string) (db *ZoneDB, err error) {
                return
        }
        // check for initialized database
-       res := db.conn.QueryRow("select name from sqlite_master where 
type='table' and name='identities'")
+       res := db.conn.QueryRow("select name from sqlite_master where 
type='table' and name='zones'")
        var s string
        if res.Scan(&s) != nil {
                // initialize database
@@ -173,152 +167,6 @@ func (db *ZoneDB) Close() error {
        return db.conn.Close()
 }
 
-//----------------------------------------------------------------------
-// Identity handling
-//----------------------------------------------------------------------
-
-// SetIdentity inserts, updates or deletes a zone in the database.
-// The function does not change timestamps which are in the
-// responsibility of the caller.
-//   - insert: Identity.ID is nil (0)
-//   - update: Identity.Name is set
-//   - remove: otherwise
-func (db *ZoneDB) SetIdentity(id *Identity) error {
-       // GNS zones are handled by Zone instances
-       if id.Svc == "gns" {
-               return db.SetZone(&id.Zone)
-       }
-       // check for identity insert
-       if id.ID == 0 {
-               stmt := "insert into 
identities(svc,name,created,modified,ztype,zdata) values(?,?,?,?,?,?)"
-               result, err := db.conn.Exec(stmt,
-                       id.Svc, id.Name, id.Created.Val, id.Modified.Val, 
id.Key.Type, id.Key.KeyData)
-               if err != nil {
-                       return err
-               }
-               id.ID, err = result.LastInsertId()
-               return err
-       }
-       // check for identity update (name and service only only)
-       if len(id.Name) > 0 {
-               stmt := "update identities set svc=?,name=?,modified=? where 
id=?"
-               result, err := db.conn.Exec(stmt, id.Svc, id.Name, 
id.Modified.Val, id.ID)
-               if err != nil {
-                       return err
-               }
-               var num int64
-               if num, err = result.RowsAffected(); err == nil {
-                       if num != 1 {
-                               err = errors.New("update identity failed")
-                       }
-               }
-               return err
-       }
-       // remove identity from database
-       _, err := db.conn.Exec("delete from identities where id=?", id.ID)
-       return err
-}
-
-// GetIdentity gets an identifier with given database id
-func (db *ZoneDB) GetIdentity(id int64) (ident *Identity, err error) {
-       // assemble identity from database row
-       stmt := "select svc,name,created,modified,ztype,zdata from identities 
where id=?"
-       ident = new(Identity)
-       ident.ID = id
-       row := db.conn.QueryRow(stmt, id)
-       var ztype enums.GNSType
-       var zdata []byte
-       if err = row.Scan(&ident.Svc, &ident.Name, &ident.Created.Val, 
&ident.Modified.Val, &ztype, &zdata); err == nil {
-               // reconstruct private zone key
-               ident.Key, err = crypto.NewZonePrivate(ztype, zdata)
-       }
-       return
-}
-
-// GetIdentity gets an identifier with given (name,svc)
-func (db *ZoneDB) GetIdentityByName(name, svc string) (ident *Identity, err 
error) {
-       // assemble identity from database row
-       var row *sql.Row
-       stmt := "select id,created,modified,ztype,zdata from identities where 
name=?"
-       if len(svc) > 0 {
-               stmt += " and svc=?"
-               row = db.conn.QueryRow(stmt, name, svc)
-       } else {
-               row = db.conn.QueryRow(stmt, name)
-       }
-       ident = new(Identity)
-       ident.Name = name
-       ident.Svc = svc
-       var ztype enums.GNSType
-       var zdata []byte
-       if err = row.Scan(&ident.ID, &ident.Created.Val, &ident.Modified.Val, 
&ztype, &zdata); err == nil {
-               // reconstruct private zone key
-               ident.Key, err = crypto.NewZonePrivate(ztype, zdata)
-       }
-       return
-}
-
-func (db *ZoneDB) GetIdentities(filter string, args ...any) (list []*Identity, 
err error) {
-       // assemble query
-       stmt := "select id,name,svc,created,modified,ztype,zdata from 
identities"
-       if len(filter) > 0 {
-               stmt += " where " + fmt.Sprintf(filter, args...)
-       }
-       // select zones
-       var rows *sql.Rows
-       if rows, err = db.conn.Query(stmt); err != nil {
-               return
-       }
-       // process zones
-       defer rows.Close()
-       for rows.Next() {
-               // assemble identity from database row
-               i := new(Identity)
-               var ztype enums.GNSType
-               var zdata []byte
-               if err = rows.Scan(&i.ID, &i.Name, &i.Svc, &i.Created.Val, 
&i.Modified.Val, &ztype, &zdata); err != nil {
-                       // terminate on error; return list so far
-                       return
-               }
-               // reconstruct private key
-               if i.Key, err = crypto.NewZonePrivate(ztype, zdata); err != nil 
{
-                       return
-               }
-               // append to result list
-               list = append(list, i)
-       }
-       return
-}
-
-func (db *ZoneDB) GetDefaultIdentity(svc string) (ident *Identity, err error) {
-       // assemble identity from database row
-       stmt := "select id,name,created,modified,ztype,zdata from v_defaults 
where svc=?"
-       row := db.conn.QueryRow(stmt, svc)
-       ident = new(Identity)
-       ident.Svc = svc
-       var ztype enums.GNSType
-       var zdata []byte
-       if err = row.Scan(&ident.ID, &ident.Name, &ident.Created.Val, 
&ident.Modified.Val, &ztype, &zdata); err == nil {
-               // reconstruct private zone key
-               ident.Key, err = crypto.NewZonePrivate(ztype, zdata)
-       }
-       return
-}
-
-func (db *ZoneDB) SetDefaultIdentity(zk *crypto.ZonePrivate, svc string) (err 
error) {
-       // get database id of identity
-       stmt := "select id from identities where zdata=?"
-       row := db.conn.QueryRow(stmt, zk.KeyData)
-       var id int64
-       if err = row.Scan(&id); err != nil {
-               return
-       }
-       // set default
-       stmt = "insert into defaults(svc,ident) values(?,?) on conflict(svc) do 
update set ident=?"
-       _, err = db.conn.Exec(stmt, svc, id, id)
-       return
-}
-
 //----------------------------------------------------------------------
 // Zone handling
 //----------------------------------------------------------------------
@@ -332,8 +180,8 @@ func (db *ZoneDB) SetDefaultIdentity(zk 
*crypto.ZonePrivate, svc string) (err er
 func (db *ZoneDB) SetZone(z *Zone) error {
        // check for zone insert
        if z.ID == 0 {
-               stmt := "insert into 
identities(svc,name,created,modified,ztype,zdata) values('gns',?,?,?,?,?)"
-               result, err := db.conn.Exec(stmt, z.Name, z.Created.Val, 
z.Modified.Val, z.Key.Type, z.Key.KeyData)
+               stmt := "insert into 
zones(name,created,modified,ztype,zdata,pdata) values(?,?,?,?,?,?)"
+               result, err := db.conn.Exec(stmt, z.Name, z.Created.Val, 
z.Modified.Val, z.Key.Type, z.Key.KeyData, z.Key.Public().KeyData)
                if err != nil {
                        return err
                }
@@ -342,7 +190,7 @@ func (db *ZoneDB) SetZone(z *Zone) error {
        }
        // check for zone update (name only)
        if len(z.Name) > 0 {
-               stmt := "update identities set name=?,modified=? where id=? and 
svc='gns'"
+               stmt := "update zones set name=?,modified=? where id=?"
                result, err := db.conn.Exec(stmt, z.Name, z.Modified.Val, z.ID)
                if err != nil {
                        return err
@@ -414,6 +262,47 @@ func (db *ZoneDB) GetZones(filter string, args ...any) 
(list []*Zone, err error)
        return
 }
 
+// GetZoneByName gets an identifier with given name
+func (db *ZoneDB) GetZoneByName(name string) (ident *Zone, err error) {
+       // assemble zone from database row
+       stmt := "select id,created,modified,ztype,zdata from zones where name=?"
+       row := db.conn.QueryRow(stmt, name)
+       ident = new(Zone)
+       ident.Name = name
+       var ztype enums.GNSType
+       var zdata []byte
+       if err = row.Scan(&ident.ID, &ident.Created.Val, &ident.Modified.Val, 
&ztype, &zdata); err == nil {
+               // reconstruct private zone key
+               ident.Key, err = crypto.NewZonePrivate(ztype, zdata)
+       }
+       return
+}
+
+// GetZoneByKey returns an identifier with given key
+func (db *ZoneDB) GetZoneByKey(zk *crypto.ZonePrivate) (ident *Zone, err 
error) {
+       // assemble zone from database row
+       stmt := "select id,name,created,modified from zones where zdata=?"
+       row := db.conn.QueryRow(stmt, zk.KeyData)
+       ident = new(Zone)
+       ident.Key = zk
+       err = row.Scan(&ident.ID, &ident.Name, &ident.Created.Val, 
&ident.Modified.Val)
+       return
+}
+
+// GetZoneByPublicKey returns an identifier with given key
+func (db *ZoneDB) GetZoneByPublicKey(zk *crypto.ZoneKey) (ident *Zone, err 
error) {
+       // assemble zone from database row
+       stmt := "select id,name,created,modified,ztype,zdata from zones where 
pdata=?"
+       row := db.conn.QueryRow(stmt, zk.KeyData)
+       ident = new(Zone)
+       var ztype enums.GNSType
+       var zdata []byte
+       if err = row.Scan(&ident.ID, &ident.Name, &ident.Created.Val, 
&ident.Modified.Val, &ztype, &zdata); err == nil {
+               ident.Key, err = crypto.NewZonePrivate(ztype, zdata)
+       }
+       return
+}
+
 //----------------------------------------------------------------------
 // Label handling
 //----------------------------------------------------------------------
@@ -427,8 +316,8 @@ func (db *ZoneDB) GetZones(filter string, args ...any) 
(list []*Zone, err error)
 func (db *ZoneDB) SetLabel(l *Label) error {
        // check for label insert
        if l.ID == 0 {
-               stmt := "insert into labels(zid,name,created,modified) 
values(?,?,?,?)"
-               result, err := db.conn.Exec(stmt, l.Zone, l.Name, 
l.Created.Val, l.Modified.Val)
+               stmt := "insert into labels(zid,name,created,modified,keyhash) 
values(?,?,?,?,?)"
+               result, err := db.conn.Exec(stmt, l.Zone, l.Name, 
l.Created.Val, l.Modified.Val, l.KeyHash.Data)
                if err != nil {
                        return err
                }
@@ -462,10 +351,54 @@ func (db *ZoneDB) SetLabel(l *Label) error {
 // GetLabel gets a label with given identifier
 func (db *ZoneDB) GetLabel(id int64) (label *Label, err error) {
        // assemble label from database row
-       stmt := "select zid,name,created,modified from labels where id=?"
+       stmt := "select zid,name,created,modified,keyhash from labels where 
id=?"
        label = new(Label)
        row := db.conn.QueryRow(stmt, id)
-       err = row.Scan(&label.Zone, &label.Name, &label.Created.Val, 
&label.Modified.Val)
+       var query []byte
+       if err = row.Scan(&label.Zone, &label.Name, &label.Created.Val, 
&label.Modified.Val, &query); err == nil {
+               label.KeyHash = crypto.NewHashCode(query)
+       }
+       return
+}
+
+// GetLabelByKeyHash returns a label with given query hash
+func (db *ZoneDB) GetLabelByKeyHash(hsh *crypto.HashCode) (label *Label, err 
error) {
+       // assemble label from database row
+       stmt := "select id,zid,name,created,modified from labels where 
keyhash=?"
+       label = new(Label)
+       label.KeyHash = hsh
+       row := db.conn.QueryRow(stmt, hsh)
+       err = row.Scan(&label.ID, &label.Zone, &label.Name, &label.Created.Val, 
&label.Modified.Val)
+       return
+}
+
+// GetLabelByName gets a label with given name and zone. Create label on
+// demand ('create' flag) if 'zid' is not 0.
+func (db *ZoneDB) GetLabelByName(name string, zid int64, create bool) (label 
*Label, err error) {
+       // assemble label from database row
+       stmt := "select id,created,modified from labels where name=? and zid=?"
+       label = new(Label)
+       label.Name = name
+       label.Zone = zid
+       row := db.conn.QueryRow(stmt, name, zid)
+       if err = row.Scan(&label.ID, &label.Created.Val, &label.Modified.Val); 
err != nil {
+               // check for "does not exist"
+               if err == sql.ErrNoRows && create {
+                       err = nil
+                       label.Created = util.AbsoluteTimeNow()
+                       label.Modified = util.AbsoluteTimeNow()
+                       if zid != 0 {
+                               // yes: create label
+                               label.Zone = zid
+                               stmt = "insert into 
labels(zid,name,created,modified) values(?,?,?,?)"
+                               var res sql.Result
+                               if res, err = db.conn.Exec(stmt, zid, name, 
label.Created.Val, label.Modified.Val); err != nil {
+                                       return
+                               }
+                               label.ID, err = res.LastInsertId()
+                       }
+               }
+       }
        return
 }
 
@@ -497,10 +430,9 @@ func (db *ZoneDB) GetLabels(filter string, args ...any) 
(list []*Label, err erro
        return
 }
 
-func (db *ZoneDB) GetLabelIDs(zk *crypto.ZonePrivate) (list []int64, err 
error) {
+func (db *ZoneDB) GetLabelIDs(zk *crypto.ZonePrivate) (list []int64, zid 
int64, err error) {
        // get zone database id
        row := db.conn.QueryRow("select id from zones where ztype=? and 
zdata=?", zk.Type, zk.KeyData)
-       var zid int64
        if err = row.Scan(&zid); err != nil {
                return
        }
@@ -579,7 +511,7 @@ func (db *ZoneDB) GetRecord(id int64) (rec *Record, err 
error) {
                return
        }
        // setup missing fields
-       rec.Size = uint32(len(rec.Data))
+       rec.Size = uint16(len(rec.Data))
        if exp != nil {
                rec.Expire.Val = *exp
        } else {
@@ -611,7 +543,7 @@ func (db *ZoneDB) GetRecords(filter string, args ...any) 
(list []*Record, err er
                        // terminate on error; return list so far
                        return
                }
-               rec.Size = uint32(len(rec.Data))
+               rec.Size = uint16(len(rec.Data))
                if exp != nil {
                        rec.Expire.Val = *exp
                } else {
diff --git a/src/gnunet/service/store/store_zonemaster.sql 
b/src/gnunet/service/store/store_zonemaster.sql
index 69b8ab5..3945f71 100644
--- a/src/gnunet/service/store/store_zonemaster.sql
+++ b/src/gnunet/service/store/store_zonemaster.sql
@@ -16,44 +16,23 @@
 --
 -- SPDX-License-Identifier: AGPL3.0-or-later
 
-create table identities (
+create table zones (
     id       integer primary key autoincrement,
-    svc      text,
-    name     text,
+    name     text unique,
     created  integer,
     modified integer,
     ztype    integer,
     zdata    blob,
-    unique (svc,name)
+    pdata    blob
 );
 
-create table defaults (
-    svc      text unique,
-    ident    integer references identities(id)
-);
-
-create view v_defaults as select
-    i.id as id,
-    d.svc as svc,
-    i.name as name,
-    i.created as created,
-    i.modified as modified,
-    i.ztype as ztype,
-    i.zdata as zdata
-from identities i, defaults d
-where i.id = d.ident;
-
-create view zones as select
-    id, name, created, modified, ztype, zdata
-from identities
-where svc = 'gns';
-
 create table labels (
     id       integer primary key autoincrement,
     zid      integer references zones(id),
        name     text,
     created  integer,
     modified integer,
+    keyhash  blob,
     unique (zid,name)
 );
 
diff --git a/src/gnunet/service/store/store_zonemaster_test.go 
b/src/gnunet/service/store/store_zonemaster_test.go
index 6f416b1..3e5e346 100644
--- a/src/gnunet/service/store/store_zonemaster_test.go
+++ b/src/gnunet/service/store/store_zonemaster_test.go
@@ -56,7 +56,9 @@ func TestZoneMaster(t *testing.T) {
        //------------------------------------------------------------------
        // create label and add to zone and database
        label := NewLabel("bar")
-       label.Zone = zone.ID
+       if err = label.SetZone(zone); err != nil {
+               t.Fatal(err)
+       }
        if err = zdb.SetLabel(label); err != nil {
                t.Fatal(err)
        }
diff --git a/src/gnunet/service/zonemaster/gui.go 
b/src/gnunet/service/zonemaster/gui.go
index 5a2f17d..0442fbf 100644
--- a/src/gnunet/service/zonemaster/gui.go
+++ b/src/gnunet/service/zonemaster/gui.go
@@ -73,6 +73,12 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) {
                "date": func(ts util.AbsoluteTime) string {
                        return guiTime(ts)
                },
+               "dateExp": func(ts util.AbsoluteTime, flags enums.GNSFlag) 
string {
+                       if flags&enums.GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+                               return guiDuration(ts)
+                       }
+                       return guiTime(ts)
+               },
                "keytype": func(t enums.GNSType) string {
                        return guiKeyType(t)
                },
@@ -81,12 +87,22 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) {
                        data["prefix"] = pf
                        if spec.Flags&enums.GNS_FLAG_PRIVATE != 0 {
                                data[pf+"private"] = "on"
+                               data[pf+"private_enforced"] = "on"
                        }
                        if spec.Flags&enums.GNS_FLAG_SHADOW != 0 {
                                data[pf+"shadow"] = "on"
+                               data[pf+"shadow_enforced"] = "on"
                        }
-                       if spec.Flags&enums.GNS_FLAG_SUPPL != 0 {
+                       if spec.Flags&enums.GNS_FLAG_SUPPLEMENTAL != 0 {
                                data[pf+"suppl"] = "on"
+                               data[pf+"suppl_enforced"] = "on"
+                       }
+                       if spec.Flags&enums.GNS_FLAG_CRITICAL != 0 {
+                               data[pf+"critical"] = "on"
+                               data[pf+"critical_enforced"] = "on"
+                       }
+                       if spec.Flags&enums.GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+                               data[pf+"ttl"] = "on"
                        }
                        return pf
                },
@@ -104,22 +120,21 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) {
                        return strings.Replace(t.String(), "GNS_TYPE_", "", -1)
                },
                "rrflags": func(f enums.GNSFlag) string {
-                       flags := make([]string, 0)
-                       if f&enums.GNS_FLAG_PRIVATE != 0 {
-                               flags = append(flags, "Private")
-                       }
-                       if f&enums.GNS_FLAG_SHADOW != 0 {
-                               flags = append(flags, "Shadow")
-                       }
-                       if f&enums.GNS_FLAG_SUPPL != 0 {
-                               flags = append(flags, "Suppl")
-                       }
+                       flags := f.List()
                        if len(flags) == 0 {
                                return "None"
                        }
                        return strings.Join(flags, ",<br>")
                },
                "rrdata": func(t enums.GNSType, buf []byte) string {
+                       // check if type is handled by plugin
+                       if plugin, ok := zm.hdlrs[t]; ok {
+                               val, err := plugin.Value(uint32(t), buf)
+                               if err != nil {
+                                       return "Failed: " + err.Error()
+                               }
+                               return val
+                       }
                        return guiRRdata(t, buf)
                },
                "tabSetList": func(num int) (list map[int]int) {
@@ -130,11 +145,20 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) {
                        return
                },
        })
+       // parse templates from embedded filesystem
        if _, err := tpl.ParseFS(fsys, "*.htpl"); err != nil {
                logger.Println(logger.ERROR, "[zonemaster] GUI templates 
failed: "+err.Error())
                return
        }
 
+       // add plugin templates
+       for _, inst := range zm.plugins {
+               if _, err := tpl.Parse(inst.Template()); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] can't process 
plugin templates: %v", err)
+                       continue
+               }
+       }
+
        // start HTTP server
        router := mux.NewRouter()
        router.HandleFunc("/new/{mode}/{id}", zm.new)
@@ -160,16 +184,28 @@ func (zm *ZoneMaster) startGUI(ctx context.Context) {
 
 //----------------------------------------------------------------------
 
+type DashboardData struct {
+       Plugins []string
+       Zones   []*store.ZoneGroup
+}
+
 // dashboard is the main entry point for the GUI
 func (zm *ZoneMaster) dashboard(w http.ResponseWriter, r *http.Request) {
+       data := new(DashboardData)
+
        // collect information for the GUI
-       zg, err := zm.zdb.GetContent()
-       if err != nil {
+       var err error
+       if data.Zones, err = zm.zdb.GetContent(); err != nil {
                _, _ = io.WriteString(w, "ERROR: "+err.Error())
                return
        }
+       // add plugin names to handle new resource records
+       data.Plugins = make([]string, 0)
+       for _, plugin := range zm.plugins {
+               data.Plugins = append(data.Plugins, plugin.Name())
+       }
        // show dashboard
-       renderPage(w, zg, "dashboard")
+       renderPage(w, data, "dashboard")
 }
 
 //======================================================================
@@ -236,9 +272,16 @@ func (zm *ZoneMaster) actionNew(w http.ResponseWriter, r 
*http.Request, mode str
        // new label
        case "label":
                name := r.FormValue("name")
+               // get zone
+               var zone *store.Zone
+               if zone, err = zm.zdb.GetZone(id); err != nil {
+                       return
+               }
                // add label to database
                label := store.NewLabel(name)
-               label.Zone = id
+               if err = label.SetZone(zone); err != nil {
+                       return
+               }
                err = zm.zdb.SetLabel(label)
 
                // notify listeners
@@ -431,13 +474,53 @@ func (zm *ZoneMaster) new(w http.ResponseWriter, r 
*http.Request) {
                // get all rrtypes used under given label
                var rrs []*enums.GNSSpec
                var label string
+               var templ string
                if rrs, label, err = zm.zdb.GetRRTypes(id); err == nil {
-                       // compile a list of acceptable types for new records
-                       data.RRspecs = compatibleRR(rrs, label)
+                       // check record mode for custom handling
+                       var mode string
+                       if modes, found := r.URL.Query()["mode"]; found {
+                               mode = modes[0]
+                       } else {
+                               mode = "GNS"
+                       }
+                       // try plugin modes first
+                       if mode != "GNS" {
+                               var pid int
+                               if pid, ok = util.CastFromString[int](mode); ok 
{
+                                       inst := zm.plugins[pid]
+                                       // convert rrs to plugin-compatible type
+                                       rrsPlugin := make([][2]uint32, 0)
+                                       for _, spec := range rrs {
+                                               rrsPlugin = append(rrsPlugin, 
[2]uint32{
+                                                       uint32(spec.Type),
+                                                       uint32(spec.Flags),
+                                               })
+                                       }
+                                       data.RRspecs = make([]*enums.GNSSpec, 0)
+                                       // let the plugin decide what is 
compatible
+                                       for _, spec := range 
inst.Compatible(label, rrsPlugin) {
+                                               s := &enums.GNSSpec{
+                                                       Type:  
enums.GNSType(spec[0]),
+                                                       Flags: 
enums.GNSFlag(spec[1]),
+                                               }
+                                               data.RRspecs = 
append(data.RRspecs, s)
+                                       }
+                                       templ, _ = inst.TemplateNames()
+                               } else {
+                                       mode = "GNS"
+                               }
+                       }
+                       // use built-in GNS (fall-back)
+                       if mode == "GNS" {
+                               // enforce GNS behaviour:
+                               // compile a list of acceptable types for new 
records
+                               data.RRspecs = compatibleRR(rrs, label)
+                               templ = "new_record"
+                       }
                        data.Ref = id
                        data.Params["label"] = label
                        data.Params["lid"] = util.CastToString(id)
-                       renderPage(w, data, "new_record")
+                       renderPage(w, data, templ)
                        return
                }
        }
@@ -507,11 +590,12 @@ func (zm *ZoneMaster) edit(w http.ResponseWriter, r 
*http.Request) {
                                return
                        }
                        // set edit parameters
-                       data.Params["zone"], _ = zm.zdb.GetName("zones", id)
+                       data.Params["zone"], _ = zm.zdb.GetName("zones", 
label.Zone)
                        data.Params["zid"] = util.CastToString(label.Zone)
                        data.Params["name"] = label.Name
                        data.Params["created"] = guiTime(label.Created)
                        data.Params["modified"] = guiTime(label.Modified)
+                       data.Params["query"] = label.KeyHash.String()
 
                        // show dialog
                        renderPage(w, data, "edit_label")
@@ -541,8 +625,16 @@ func (zm *ZoneMaster) editRec(w http.ResponseWriter, r 
*http.Request, data *NewE
        if rec, err = zm.zdb.GetRecord(data.Ref); err != nil {
                return
        }
-       // build map of attribute values
-       pf := dlgPrefix[rec.RType]
+       // get prefix used for attributes and fields
+       pf, ok := dlgPrefix[rec.RType]
+       if !ok {
+               // no prefix defined; ask plugin
+               inst, ok := zm.hdlrs[rec.RType]
+               if !ok {
+                       return errors.New("no prefix defined for record type")
+               }
+               pf = inst.Prefix(uint32(rec.RType)) + "_"
+       }
 
        // save shared attributes
        data.Params["prefix"] = pf
@@ -551,10 +643,15 @@ func (zm *ZoneMaster) editRec(w http.ResponseWriter, r 
*http.Request, data *NewE
        data.Params["modified"] = guiTime(rec.Modified)
        data.Params["label"], _ = zm.zdb.GetName("labels", rec.Label)
        data.Params["lid"] = util.CastToString(rec.Label)
-       if rec.Expire.IsNever() {
-               data.Params[pf+"never"] = "on"
+       if rec.Flags&enums.GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+               data.Params[pf+"ttl"] = "on"
+               data.Params[pf+"ttl_value"] = guiDuration(rec.Expire)
        } else {
-               data.Params[pf+"expires"] = htmlTime(rec.Expire)
+               if rec.Expire.IsNever() {
+                       data.Params[pf+"never"] = "on"
+               } else {
+                       data.Params[pf+"expires"] = htmlTime(rec.Expire)
+               }
        }
        if rec.Flags&enums.GNS_FLAG_PRIVATE != 0 {
                data.Params[pf+"private"] = "on"
@@ -562,9 +659,12 @@ func (zm *ZoneMaster) editRec(w http.ResponseWriter, r 
*http.Request, data *NewE
        if rec.Flags&enums.GNS_FLAG_SHADOW != 0 {
                data.Params[pf+"shadow"] = "on"
        }
-       if rec.Flags&enums.GNS_FLAG_SUPPL != 0 {
+       if rec.Flags&enums.GNS_FLAG_SUPPLEMENTAL != 0 {
                data.Params[pf+"suppl"] = "on"
        }
+       if rec.Flags&enums.GNS_FLAG_CRITICAL != 0 {
+               data.Params[pf+"critical"] = "on"
+       }
        // get record instance
        var inst rr.RR
        if inst, err = rr.ParseRR(rec.RType, rec.Data); err == nil {
diff --git a/src/gnunet/service/zonemaster/gui.htpl 
b/src/gnunet/service/zonemaster/gui.htpl
index de51ee3..36e08f3 100644
--- a/src/gnunet/service/zonemaster/gui.htpl
+++ b/src/gnunet/service/zonemaster/gui.htpl
@@ -32,8 +32,9 @@
 {{define "dashboard"}}
 <div>
     <ul id="dashboard">
-    {{if .}}
-        {{range $zi, $zone := .}}
+    {{if .Zones}}
+        {{$plugins := .Plugins}}
+        {{range $zi, $zone := .Zones}}
         <li>
             {{$z := $zone.Zone}}
             <span class="caret"><b>{{$z.Name}}</b></span> [{{keytype 
$z.Key.Type}}: {{$zone.PubID}}]
@@ -67,7 +68,7 @@
                                     <td>{{rrtype $rec.RType}}</td>
                                     <td>{{rrdata $rec.RType $rec.Data}}</td>
                                     <td>{{rrflags $rec.Flags}}</td>
-                                    <td>{{date $rec.Expire}}</td>
+                                    <td>{{dateExp $rec.Expire $rec.Flags}}</td>
                                     <td>{{date $rec.Created}}</td>
                                     <td>{{date $rec.Modified}}</td>
                                     <td>
@@ -82,7 +83,15 @@
                         <li><h3>No resource records for label defined 
yet.</h3></li>
                     {{end}}
                         <li>
-                            <a href="/new/rr/{{$l.ID}}" title="Add new 
record..."><button class="icon blue">&#10010;</button></a>
+                            <form action="/new/rr/{{$l.ID}}" method="get">
+                                <select name="mode">
+                                    <option value="GNS" selected>GNS</option>
+                                    {{range $i,$v := $plugins}}
+                                    <option value="{{$i}}">{{$v}}</option>
+                                    {{end}}
+                                </select>
+                                <button id="submit" class="icon 
blue">&#10010;</button>
+                            </form>
                         </li>
                     </ul>
                 </li>
diff --git a/src/gnunet/service/zonemaster/gui_css.htpl 
b/src/gnunet/service/zonemaster/gui_css.htpl
index 8a26b30..8dfac9b 100644
--- a/src/gnunet/service/zonemaster/gui_css.htpl
+++ b/src/gnunet/service/zonemaster/gui_css.htpl
@@ -230,5 +230,17 @@
     input.alternate:checked ~ div.alternate {
         display: none;
     }
+    div.toggle-on {
+        display: none;
+    }
+    div.toggle-off {
+        display: block;
+    }
+    input.toggle:checked ~ div.toggle-on {
+        display: block;
+    }
+    input.toggle:checked ~ div.toggle-off {
+        display: none;
+    }
 </style>
 {{end}}
diff --git a/src/gnunet/service/zonemaster/gui_edit.htpl 
b/src/gnunet/service/zonemaster/gui_edit.htpl
index a4673b0..d1f30de 100644
--- a/src/gnunet/service/zonemaster/gui_edit.htpl
+++ b/src/gnunet/service/zonemaster/gui_edit.htpl
@@ -66,6 +66,7 @@
     <div>
         <h3>Edit a GNS label for zone "{{$zone}}":</h3>
         <p><small>(Created: {{index .Params "created"}}, Last edited: {{index 
.Params "modified"}})</small></p>
+        <p>Query hash({{$name}}): {{index .Params "query"}}</p>
         <form action="/action/upd/label/{{.Ref}}" method="post" 
onsubmit="return(label_validate());">
             <input type="hidden" name="old_name" value="{{$name}}">
             <input type="hidden" name="zid" value="{{index .Params "zid"}}">
diff --git a/src/gnunet/service/zonemaster/gui_plugin.go 
b/src/gnunet/service/zonemaster/gui_plugin.go
new file mode 100644
index 0000000..97490a4
--- /dev/null
+++ b/src/gnunet/service/zonemaster/gui_plugin.go
@@ -0,0 +1,68 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019-2022 Bernd Fix  >Y<
+//
+// gnunet-go is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// gnunet-go is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package zonemaster
+
+import (
+       "gnunet/enums"
+)
+
+// ZoneMasterUtility can perform utility functions on behalf of plugins
+var ZoneMasterUtility = func(fcn string, args ...any) any {
+       switch fcn {
+       case "gns_type_name":
+               return enums.GNSType(args[0].(uint32)).String()
+       case "gns_flags":
+               return enums.GNSFlag(args[0].(uint32)).List()
+       }
+       return nil
+}
+
+// ZoneMasterPlugin handles resource record type specific functionality
+type Plugin interface {
+       // SetUtility passes a utility function to plugins
+       // (Callback-like function)
+       SetUtility(any)
+
+       // Name of the plugin
+       Name() string
+
+       // CanHandle returns a list of resource record types
+       CanHandle() []uint32
+
+       // Compute a set of record specs allowed under a label with existing 
records
+       Compatible(label string, rrSpecs [][2]uint32) [][2]uint32
+
+       // Value returns a human-readable description of RR data
+       Value(t uint32, rr []byte) (string, error)
+
+       // Template returns the new / edit template for custom types
+       Template() string
+
+       // TemplateNames returns the names for the "new" and "edit" dialogs
+       TemplateNames() (string, string)
+
+       // Prefix returns the prefix for record attributes in map
+       Prefix(t uint32) string
+
+       // ToMap converts resource record data into GUI template variables
+       ToMap(t uint32, rr []byte) (map[string]string, error)
+
+       // FromMap converts a GUI template variables into resource record data
+       FromMap(t uint32, vars map[string]string) ([]byte, error)
+}
diff --git a/src/gnunet/service/zonemaster/gui_rr.htpl 
b/src/gnunet/service/zonemaster/gui_rr.htpl
index 00b9148..06b926a 100644
--- a/src/gnunet/service/zonemaster/gui_rr.htpl
+++ b/src/gnunet/service/zonemaster/gui_rr.htpl
@@ -7,13 +7,24 @@
     <tr>
         <td align="right" valign="top"><b>Expires:</b></td>
         <td>
-            Never <input type="checkbox" class="alternate" name="{{$pf}}never"
-                {{if eq "on" (index .Params (print $pf 
"never"))}}checked="checked"{{end}}
+            TTL <input type="checkbox" class="toggle" name="{{$pf}}ttl"
+                {{if eq "on" (index .Params (print $pf 
"ttl"))}}checked="checked"{{end}}
             >
-            <div class="alternate">
-                At given date and time:
-                <input type="datetime-local" id="{{$pf}}expires" 
name="{{$pf}}expires" required
-                    value="{{index .Params (print $pf "expires")}}"
+            <div class="toggle-off">
+                Never <input type="checkbox" class="alternate" 
name="{{$pf}}never"
+                    {{if eq "on" (index .Params (print $pf 
"never"))}}checked="checked"{{end}}
+                >
+                <div class="alternate">
+                    At given date and time:
+                    <input type="datetime-local" id="{{$pf}}expires" 
name="{{$pf}}expires" required
+                        value="{{index .Params (print $pf "expires")}}"
+                    >
+                </div>
+            </div>
+            <div class="toggle-on">
+                Duration: <input type="text" name="{{$pf}}ttl_value"
+                    pattern="([0-9]*h)?([0-9]*m)?([0-9]*s)?"
+                    value="{{index .Params (print $pf "ttl_value")}}"
                 >
             </div>
         </td>
@@ -22,14 +33,21 @@
         <td align="right" valign="top"><b>Flags:</b></td>
         <td>
             <input type="checkbox" name="{{$pf}}private"
-                {{if eq "on" (index .Params (print $pf 
"private"))}}checked="checked" class="disabled"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"private"))}}checked="checked"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"private_enforced"))}}class="disabled"{{end}}
                 > Private<br>
             <input type="checkbox" name="{{$pf}}shadow"
-                {{if eq "on" (index .Params (print $pf 
"shadow"))}}checked="checked" class="disabled"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"shadow"))}}checked="checked"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"shadow_enforced"))}}class="disabled"{{end}}
                 > Shadow<br>
             <input type="checkbox" name="{{$pf}}suppl"
-                {{if eq "on" (index .Params (print $pf 
"suppl"))}}checked="checked" class="disabled"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"suppl"))}}checked="checked"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"suppl_enforced"))}}class="disabled"{{end}}
                 > Supplemental<br>
+            <input type="checkbox" name="{{$pf}}critical"
+                {{if eq "on" (index .Params (print $pf 
"critical"))}}checked="checked"{{end}}
+                {{if eq "on" (index .Params (print $pf 
"critical_enforced"))}}class="disabled"{{end}}
+                > Critical<br>
         </td>
     </tr>
     {{if eq .Action "new"}}
diff --git a/src/gnunet/service/zonemaster/records.go 
b/src/gnunet/service/zonemaster/records.go
index a10ee05..a24988c 100644
--- a/src/gnunet/service/zonemaster/records.go
+++ b/src/gnunet/service/zonemaster/records.go
@@ -22,7 +22,6 @@ import (
        "encoding/hex"
        "errors"
        "fmt"
-       "gnunet/crypto"
        "gnunet/enums"
        "gnunet/service/dht/blocks"
        "gnunet/service/gns/rr"
@@ -87,6 +86,19 @@ func htmlTime(ts util.AbsoluteTime) string {
        return time.UnixMicro(int64(ts.Val)).Format(timeHTML)
 }
 
+func parseDuration(s string) uint64 {
+       d, err := time.ParseDuration(s)
+       if err != nil {
+               return 3600000000 // 1 hour default
+       }
+       return uint64(d.Microseconds())
+}
+
+func guiDuration(ts util.AbsoluteTime) string {
+       d := time.Duration(ts.Val) * time.Microsecond
+       return d.String()
+}
+
 func guiTime(ts util.AbsoluteTime) string {
        if ts.IsNever() {
                return "Never"
@@ -109,7 +121,7 @@ func guiRRdata(t enums.GNSType, buf []byte) string {
        // get record instance
        inst, err := rr.ParseRR(t, buf)
        if err != nil {
-               return "<unknown>"
+               return "(invalid)"
        }
        // type-dependent rendering
        switch rec := inst.(type) {
@@ -177,10 +189,15 @@ func guiPrefix(t enums.GNSType) string {
 // parse expiration time and flags from GUI parameters
 func guiParse(params map[string]string, pf string) (exp util.AbsoluteTime, 
flags enums.GNSFlag) {
        // parse expiration time
-       exp = util.AbsoluteTimeNever()
-       if _, ok := params[pf+"never"]; !ok {
-               ts, _ := time.Parse(timeHTML, params[pf+"expires"])
-               exp.Val = uint64(ts.UnixMicro())
+       if _, ok := params[pf+"ttl"]; ok {
+               flags |= enums.GNS_FLAG_RELATIVE_EXPIRATION
+               exp.Val = parseDuration(params[pf+"ttl_value"])
+       } else {
+               exp = util.AbsoluteTimeNever()
+               if _, ok := params[pf+"never"]; !ok {
+                       ts, _ := time.Parse(timeHTML, params[pf+"expires"])
+                       exp.Val = uint64(ts.UnixMicro())
+               }
        }
        // parse flags
        flags = 0
@@ -191,7 +208,10 @@ func guiParse(params map[string]string, pf string) (exp 
util.AbsoluteTime, flags
                flags |= enums.GNS_FLAG_SHADOW
        }
        if _, ok := params[pf+"suppl"]; ok {
-               flags |= enums.GNS_FLAG_SUPPL
+               flags |= enums.GNS_FLAG_SUPPLEMENTAL
+       }
+       if _, ok := params[pf+"critical"]; ok {
+               flags |= enums.GNS_FLAG_CRITICAL
        }
        return
 }
@@ -351,7 +371,7 @@ func compatibleRR(in []*enums.GNSSpec, label string) (out 
[]*enums.GNSSpec) {
 }
 
 // get a list of resource records for a given label in a zone.
-func (zm *ZoneMaster) getRecords(zk *crypto.ZoneKey, label int64) (rs 
*blocks.RecordSet, expire util.AbsoluteTime, err error) {
+func (zm *ZoneMaster) GetRecordSet(label int64, filter enums.GNSFilter) (rs 
*blocks.RecordSet, expire util.AbsoluteTime, err error) {
        // collect records for zone label
        var recs []*store.Record
        if recs, err = zm.zdb.GetRecords("lid=%d", label); err != nil {
@@ -361,7 +381,12 @@ func (zm *ZoneMaster) getRecords(zk *crypto.ZoneKey, label 
int64) (rs *blocks.Re
        expire = util.AbsoluteTimeNever()
        rs = blocks.NewRecordSet()
        for _, r := range recs {
-               if r.Expire.Compare(expire) < 0 {
+               // filter out records
+               if filter&enums.GNS_FILTER_OMIT_PRIVATE != 0 && 
r.Flags&enums.GNS_FLAG_PRIVATE != 0 {
+                       continue
+               }
+               // skip TTL expiry when determining earliest expiry
+               if r.Flags&enums.GNS_FLAG_RELATIVE_EXPIRATION == 0 && 
r.Expire.Compare(expire) < 0 {
                        expire = r.Expire
                }
                rs.AddRecord(&r.ResourceRecord)
diff --git a/src/gnunet/service/zonemaster/service.go 
b/src/gnunet/service/zonemaster/service.go
index 5725ec8..399b5d8 100644
--- a/src/gnunet/service/zonemaster/service.go
+++ b/src/gnunet/service/zonemaster/service.go
@@ -25,7 +25,6 @@ import (
 
        "gnunet/config"
        "gnunet/core"
-       "gnunet/enums"
        "gnunet/message"
        "gnunet/service"
        "gnunet/service/dht/blocks"
@@ -89,135 +88,39 @@ func (zm *ZoneMaster) ServeClient(ctx context.Context, id 
int, mc *service.Conne
 // Handle a single incoming message
 func (zm *ZoneMaster) HandleMessage(ctx context.Context, sender *util.PeerID, 
msg message.Message, back transport.Responder) bool {
        // assemble log label
-       var id int
        var label string
        if v := ctx.Value(core.CtxKey("params")); v != nil {
                if ps, ok := v.(util.ParameterSet); ok {
                        label, _ = util.GetParam[string](ps, "label")
-                       id, _ = util.GetParam[int](ps, "id")
                }
        }
        // perform lookup
-       switch m := msg.(type) {
+       switch msg.(type) {
 
        //------------------------------------------------------------------
        // Identity service
        //------------------------------------------------------------------
 
-       // start identity update listener
-       case *message.IdentityStartMsg:
-               if err := zm.identity.Start(ctx, id); err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
session for %d failed: %v\n", label, id, err)
-                       return false
-               }
-
-       // create a new identity with given private key
-       case *message.IdentityCreateMsg:
-               if err := zm.identity.Create(ctx, id, m.ZoneKey, m.Name()); err 
!= nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
create failed: %v\n", label, err)
-                       return false
-               }
-
-       // rename identity
-       case *message.IdentityRenameMsg:
-               id, err := zm.zdb.GetIdentityByName(m.OldName(), 
IDENT_DEFAULT_SERVICE)
-               if err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
lookup failed: %v\n", label, err)
-                       return false
-               }
-               // change name
-               id.Name = m.NewName()
-               err = zm.zdb.SetIdentity(id)
-
-               // send response
-               rc := enums.RC_OK
-               msg := ""
-               if err != nil {
-                       rc = enums.RC_NO
-                       msg = err.Error()
-               }
-               resp := message.NewIdentityResultCodeMsg(rc, msg)
-               if err = back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[identity:%s] Can't send 
response (%v): %v\n", label, resp, err)
-               }
-
-       // delete identity
-       case *message.IdentityDeleteMsg:
-               id, err := zm.zdb.GetIdentityByName(m.Name(), 
IDENT_DEFAULT_SERVICE)
-               if err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
lookup failed: %v\n", label, err)
-                       return false
-               }
-               // delete in database
-               id.Name = ""
-               err = zm.zdb.SetIdentity(id)
-
-               // send response
-               rc := enums.RC_OK
-               msg := ""
-               if err != nil {
-                       rc = enums.RC_NO
-                       msg = err.Error()
-               }
-               resp := message.NewIdentityResultCodeMsg(rc, msg)
-               if err = back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[identity:%s] Can't send 
response (%v): %v\n", label, resp, err)
-               }
-
-       // lookup identity
-       case *message.IdentityLookupMsg:
-               id, err := zm.zdb.GetIdentityByName(m.Name, 
IDENT_DEFAULT_SERVICE)
-               if err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
lookup failed: %v\n", label, err)
-                       return false
-               }
-               resp := message.NewIdentityUpdateMsg(id.Name, id.Key)
-               logger.Printf(logger.DBG, "[identity:%s] Sending %v", label, 
resp)
-               if err = back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[identity:%s] Can't send 
response (%v): %v\n", label, resp, err)
-               }
-
-       // get default identity for service
-       case *message.IdentityGetDefaultMsg:
-               id, err := zm.zdb.GetDefaultIdentity(m.Service())
-               if err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Identity 
lookup failed: %v\n", label, err)
-                       return false
-               }
-               resp := message.NewIdentityUpdateMsg(id.Name, id.Key)
-               logger.Printf(logger.DBG, "[identity:%s] Sending %v", label, 
resp)
-               if err = back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[identity:%s] Can't send 
response (%v): %v\n", label, resp, err)
-               }
-
-       // set default identity for service
-       case *message.IdentitySetDefaultMsg:
-               err := zm.zdb.SetDefaultIdentity(m.ZoneKey, m.Service())
-
-               // send response
-               rc := enums.RC_OK
-               msg := ""
-               if err != nil {
-                       rc = enums.RC_NO
-                       msg = err.Error()
-               }
-               resp := message.NewIdentityResultCodeMsg(rc, msg)
-               if err = back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[identity:%s] Can't send 
response (%v): %v\n", label, resp, err)
-               }
+       case *message.IdentityStartMsg,
+               *message.IdentityCreateMsg,
+               *message.IdentityRenameMsg,
+               *message.IdentityDeleteMsg,
+               *message.IdentityLookupMsg:
+               zm.identity.HandleMessage(ctx, sender, msg, back)
 
        //------------------------------------------------------------------
        // Namestore service
        //------------------------------------------------------------------
 
-       // start new zone iteration
-       case *message.NamestoreZoneIterStartMsg:
-               iter := zm.namestore.NewIterator(m.ID, m.ZoneKey)
-               resp := iter.Next()
-               if err := back.Send(ctx, resp); err != nil {
-                       logger.Printf(logger.ERROR, "[zonemaster%s] Can't send 
response (%v)\n", label, resp)
-                       return false
-               }
+       case *message.NamestoreZoneIterStartMsg,
+               *message.NamestoreZoneIterNextMsg,
+               *message.NamestoreRecordStoreMsg,
+               *message.NamestoreRecordLookupMsg,
+               *message.NamestoreZoneToNameMsg,
+               *message.NamestoreZoneToNameRespMsg,
+               *message.NamestoreMonitorStartMsg,
+               *message.NamestoreMonitorNextMsg:
+               zm.namestore.HandleMessage(ctx, sender, msg, back)
 
        default:
                //----------------------------------------------------------
@@ -250,3 +153,12 @@ func (zm *ZoneMaster) StoreNamecache(ctx context.Context, 
query *blocks.GNSQuery
        _, err = service.RequestResponse(ctx, "zonemaster", "namecache", 
config.Cfg.Namecache.Service.Socket, req, false)
        return
 }
+
+func sendResponse(ctx context.Context, label string, resp message.Message, 
back transport.Responder) bool {
+       logger.Printf(logger.DBG, "[%s] Sending %v", label, resp)
+       if err := back.Send(ctx, resp); err != nil {
+               logger.Printf(logger.ERROR, "[%s] Can't send response (%v)\n", 
label, resp)
+               return false
+       }
+       return true
+}
diff --git a/src/gnunet/service/zonemaster/service_identity.go 
b/src/gnunet/service/zonemaster/service_identity.go
index b7777be..3d37cee 100644
--- a/src/gnunet/service/zonemaster/service_identity.go
+++ b/src/gnunet/service/zonemaster/service_identity.go
@@ -21,8 +21,8 @@ package zonemaster
 import (
        "context"
        "fmt"
+       "gnunet/core"
        "gnunet/crypto"
-       "gnunet/enums"
        "gnunet/message"
        "gnunet/service/store"
        "gnunet/transport"
@@ -31,11 +31,6 @@ import (
        "github.com/bfix/gospel/logger"
 )
 
-//nolint:stylecheck // my style is my style...
-const (
-       IDENT_DEFAULT_SERVICE = "ego"
-)
-
 //----------------------------------------------------------------------
 // "GNUnet Identity" service implementation:
 //----------------------------------------------------------------------
@@ -89,8 +84,8 @@ func (ident *IdentityService) Start(ctx context.Context, id 
int) (err error) {
                return
        }
        // initial update is to send all existing identites
-       var list []*store.Identity
-       if list, err = ident.zm.zdb.GetIdentities(""); err != nil {
+       var list []*store.Zone
+       if list, err = ident.zm.zdb.GetZones(""); err != nil {
                return
        }
        for _, ident := range list {
@@ -118,18 +113,101 @@ func (ident *IdentityService) Create(ctx 
context.Context, cid int, zk *crypto.Zo
                return
        }
        // add identity
-       id := store.NewIdentity(name, zk, IDENT_DEFAULT_SERVICE)
-       err = ident.zm.zdb.SetIdentity(id)
-       rc := enums.RC_OK
-       msg := ""
+       id := store.NewZone(name, zk)
+       err = ident.zm.zdb.SetZone(id)
+       rc := 0
        if err != nil {
-               rc = enums.RC_NO
-               msg = err.Error()
+               rc = 1
        }
-       resp := message.NewIdentityResultCodeMsg(rc, msg)
+       resp := message.NewIdentityResultCodeMsg(rc)
        if err = sess.back.Send(ctx, resp); err != nil {
                logger.Printf(logger.ERROR, "[identity:%d] Can't send response 
(%v): %v\n", cid, resp, err)
                return
        }
        return
 }
+
+// HandleMessage processes a single incoming message
+func (ident *IdentityService) HandleMessage(ctx context.Context, sender 
*util.PeerID, msg message.Message, back transport.Responder) bool {
+       // assemble log label
+       var id int
+       var label string
+       if v := ctx.Value(core.CtxKey("params")); v != nil {
+               if ps, ok := v.(util.ParameterSet); ok {
+                       label, _ = util.GetParam[string](ps, "label")
+                       id, _ = util.GetParam[int](ps, "id")
+               }
+       }
+       // perform lookup
+       switch m := msg.(type) {
+
+       // start identity update listener
+       case *message.IdentityStartMsg:
+               if err := ident.Start(ctx, id); err != nil {
+                       logger.Printf(logger.ERROR, "[identity%s] Identity 
session for %d failed: %v\n", label, id, err)
+                       return false
+               }
+
+       // create a new identity with given private key
+       case *message.IdentityCreateMsg:
+               if err := ident.Create(ctx, id, m.ZoneKey, m.Name()); err != 
nil {
+                       logger.Printf(logger.ERROR, "[identity%s] Identity 
create failed: %v\n", label, err)
+                       return false
+               }
+
+       // rename identity
+       case *message.IdentityRenameMsg:
+               id, err := ident.zm.zdb.GetZoneByName(m.OldName())
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[identity%s] Identity 
lookup failed: %v\n", label, err)
+                       return false
+               }
+               // change name
+               id.Name = m.NewName()
+               err = ident.zm.zdb.SetZone(id)
+
+               // send response
+               rc := 0
+               if err != nil {
+                       rc = 1
+               }
+               resp := message.NewIdentityResultCodeMsg(rc)
+               if !sendResponse(ctx, "identity"+label, resp, back) {
+                       return false
+               }
+
+       // delete identity
+       case *message.IdentityDeleteMsg:
+               id, err := ident.zm.zdb.GetZoneByName(m.Name())
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[identity%s] Identity 
lookup failed: %v\n", label, err)
+                       return false
+               }
+               // delete in database
+               id.Name = ""
+               err = ident.zm.zdb.SetZone(id)
+
+               // send response
+               rc := 0
+               if err != nil {
+                       rc = 1
+               }
+               resp := message.NewIdentityResultCodeMsg(rc)
+               if !sendResponse(ctx, "identity"+label, resp, back) {
+                       return false
+               }
+
+       // lookup identity
+       case *message.IdentityLookupMsg:
+               id, err := ident.zm.zdb.GetZoneByName(m.Name)
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[identity%s] Identity 
lookup failed: %v\n", label, err)
+                       return false
+               }
+               resp := message.NewIdentityUpdateMsg(id.Name, id.Key)
+               if !sendResponse(ctx, "identity"+label, resp, back) {
+                       return false
+               }
+       }
+       return true
+}
diff --git a/src/gnunet/service/zonemaster/service_namestore.go 
b/src/gnunet/service/zonemaster/service_namestore.go
index 2c6d7d1..be24142 100644
--- a/src/gnunet/service/zonemaster/service_namestore.go
+++ b/src/gnunet/service/zonemaster/service_namestore.go
@@ -19,59 +19,99 @@
 package zonemaster
 
 import (
+       "context"
+       "gnunet/core"
        "gnunet/crypto"
+       "gnunet/enums"
        "gnunet/message"
+       "gnunet/service/dht/blocks"
        "gnunet/service/store"
+       "gnunet/transport"
        "gnunet/util"
+
+       "github.com/bfix/gospel/logger"
 )
 
-//----------------------------------------------------------------------
+//======================================================================
 // "GNUnet Namestore" service implementation:
+//======================================================================
+
+//----------------------------------------------------------------------
+// Zone iterator
 //----------------------------------------------------------------------
 
+// ZoneIterator is used to traverse all labels in a zone
 type ZoneIterator struct {
-       id       uint32
-       zk       *crypto.ZonePrivate
-       lastUsed util.AbsoluteTime
-       db       *store.ZoneDB
-
-       labels []int64
-       pos    int
+       id       uint32              // request ID
+       zid      int64               // database ID of zone
+       zk       *crypto.ZonePrivate // private zone key
+       lastUsed util.AbsoluteTime   // last time iterator was used
+       zm       *ZoneMaster         // reference to zone master
+       labels   []int64             // list of label ids in database for zone
+       pos      int                 // iteration step
 }
 
-func NewZoneIterator(id uint32, zk *crypto.ZonePrivate, db *store.ZoneDB) (zi 
*ZoneIterator, err error) {
+// NewZoneIterator initialize an iterator to traverse the zone labels
+func NewZoneIterator(id uint32, zk *crypto.ZonePrivate, zm *ZoneMaster) (zi 
*ZoneIterator, err error) {
        // get list of labels to handle
        var labels []int64
-       if labels, err = db.GetLabelIDs(zk); err != nil {
+       var zid int64
+       if labels, zid, err = zm.zdb.GetLabelIDs(zk); err != nil {
                return
        }
        // assemble zone iterator
        zi = &ZoneIterator{
                id:       id,
+               zid:      zid,
                zk:       zk,
                lastUsed: util.AbsoluteTimeNow(),
-               db:       db,
+               zm:       zm,
                pos:      0,
                labels:   labels,
        }
        return
 }
 
-func (zi *ZoneIterator) Next() *message.NamestoreRecordResultMsg {
-       if zi.pos == len(zi.labels)-1 {
-               // end of list reached
-               return nil
+// Next returns the next record
+func (zi *ZoneIterator) Next() (msg message.Message, done bool) {
+       if zi.pos == len(zi.labels) {
+               // end of list reached:
+               msg = message.NewNamestoreZoneIterEndMsg(zi.id)
+               done = true
+               return
        }
-
-       return nil
+       // get resource records
+       lid := zi.labels[zi.pos]
+       zi.pos++
+       lbl, err := zi.zm.zdb.GetLabel(lid)
+       if err != nil {
+               logger.Printf(logger.ERROR, "[zone_iter] label name: %s", 
err.Error())
+               return
+       }
+       rrSet, expire, err := zi.zm.GetRecordSet(lid, enums.GNS_FILTER_NONE)
+       if err != nil {
+               logger.Printf(logger.ERROR, "[zone_iter] records: %s", 
err.Error())
+               return
+       }
+       // assemble response
+       rmsg := message.NewNamestoreRecordResultMsg(zi.id, zi.zk, lbl.Name)
+       rmsg.Expire = expire
+       rmsg.AddRecords(rrSet)
+       msg = rmsg
+       return
 }
 
+//----------------------------------------------------------------------
+// Namestore service
+//----------------------------------------------------------------------
+
 // NamestoreService to handle namestore requests
 type NamestoreService struct {
        zm    *ZoneMaster
        iters *util.Map[uint32, *ZoneIterator]
 }
 
+// NewNamestoreService creates a new namestore service handler
 func NewNamestoreService(zm *ZoneMaster) *NamestoreService {
        return &NamestoreService{
                zm:    zm,
@@ -79,12 +119,194 @@ func NewNamestoreService(zm *ZoneMaster) 
*NamestoreService {
        }
 }
 
+// NewIterator creates a new iterator for zone traversal
 func (s *NamestoreService) NewIterator(id uint32, zk *crypto.ZonePrivate) 
*ZoneIterator {
-       zi := &ZoneIterator{
-               id:       id,
-               zk:       zk,
-               lastUsed: util.AbsoluteTimeNow(),
+       zi, err := NewZoneIterator(id, zk, s.zm)
+       if err != nil {
+               logger.Printf(logger.ERROR, "[namestore] new zone iterator: 
%s", err.Error())
+               return nil
        }
        s.iters.Put(id, zi, 0)
        return zi
 }
+
+// GetIterator returns the iterator for request ID
+func (s *NamestoreService) GetIterator(id uint32) (*ZoneIterator, bool) {
+       return s.iters.Get(id, 0)
+}
+
+// DropIterator removes the iterator for request ID
+func (s *NamestoreService) DropIterator(id uint32) {
+       s.iters.Delete(id, 0)
+}
+
+// Store labeled recordsets to zone
+func (s *NamestoreService) Store(zk *crypto.ZonePrivate, list 
[]*message.NamestoreRecordSet) bool {
+       // get the zone with given key
+       zone, err := s.zm.zdb.GetZoneByKey(zk)
+       if err != nil {
+               logger.Printf(logger.ERROR, "[namestore] zone from key: %s", 
err.Error())
+               return false
+       }
+       // add all record sets
+       for _, entry := range list {
+               // get labeled resource records
+               label, _ := util.ReadCString(entry.Name, 0)
+               // get label object from database
+               var lbl *store.Label
+               if lbl, err = s.zm.zdb.GetLabelByName(label, zone.ID, true); 
err != nil {
+                       logger.Printf(logger.ERROR, "[namestore] label from 
name: %s", err.Error())
+                       return false
+               }
+               // disassemble record set data
+               rr, err := blocks.NewRecordSetFromRDATA(uint32(entry.RdCount), 
entry.RecData)
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[namestore] record from 
data: %s", err.Error())
+                       return false
+               }
+               for _, rr := range rr.Records {
+                       // assemble record and store in database
+                       rec := store.NewRecord(rr.Expire, rr.RType, rr.Flags, 
rr.Data)
+                       rec.Label = lbl.ID
+                       if err = s.zm.zdb.SetRecord(rec); err != nil {
+                               logger.Printf(logger.ERROR, "[namestore] add 
record: %s", err.Error())
+                               return false
+                       }
+               }
+       }
+       return true
+}
+
+// HandleMessage processes a single incoming message
+func (s *NamestoreService) HandleMessage(ctx context.Context, sender 
*util.PeerID, msg message.Message, back transport.Responder) bool {
+       // assemble log label
+       var label string
+       if v := ctx.Value(core.CtxKey("params")); v != nil {
+               if ps, ok := v.(util.ParameterSet); ok {
+                       label, _ = util.GetParam[string](ps, "label")
+               }
+       }
+       // perform lookup
+       switch m := msg.(type) {
+
+       // start new zone iteration
+       case *message.NamestoreZoneIterStartMsg:
+               // setup iterator
+               iter := s.NewIterator(m.ID, m.ZoneKey)
+               // return first result
+               resp, done := iter.Next()
+               if done {
+                       s.DropIterator(m.ID)
+               }
+               if !sendResponse(ctx, "namestore"+label, resp, back) {
+                       return false
+               }
+
+       // next label(s) from zone iteration
+       case *message.NamestoreZoneIterNextMsg:
+               var resp message.Message
+               // lookup zone iterator
+               iter, ok := s.GetIterator(m.ID)
+               if !ok {
+                       zk, _ := crypto.NullZonePrivate(enums.GNS_TYPE_PKEY)
+                       resp = message.NewNamestoreRecordResultMsg(m.ID, zk, "")
+                       if !sendResponse(ctx, "namestore"+label, resp, back) {
+                               return false
+                       }
+               } else {
+                       for n := 0; n < int(m.Limit); n++ {
+                               // return next result
+                               var done bool
+                               resp, done = iter.Next()
+                               if !sendResponse(ctx, "namestore"+label, resp, 
back) {
+                                       return false
+                               }
+                               if done {
+                                       s.DropIterator(m.ID)
+                                       break
+                               }
+                       }
+               }
+
+       // store record in zone database
+       case *message.NamestoreRecordStoreMsg:
+               var rc uint32
+               if !s.Store(m.ZoneKey, m.RSets) {
+                       rc = 1
+               }
+               resp := message.NewNamestoreRecordStoreRespMsg(m.ID, rc)
+               if !sendResponse(ctx, "namestore"+label, resp, back) {
+                       return false
+               }
+
+       // lookup records in zone under given label
+       case *message.NamestoreRecordLookupMsg:
+               // get resource records
+               getRecs := func() *blocks.RecordSet {
+                       zone, err := s.zm.zdb.GetZoneByKey(m.ZoneKey)
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[namestore%s] zone 
lookup: %s", label, err.Error())
+                               return nil
+                       }
+                       lbl, err := s.zm.zdb.GetLabelByName(string(m.Label), 
zone.ID, false)
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[namestore%s] 
label lookup: %s", label, err.Error())
+                               return nil
+                       }
+                       rrSet, _, err := s.zm.GetRecordSet(lbl.ID, 
enums.GNSFilter(m.Filter))
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[namestore%s] 
records: %s", label, err.Error())
+                               return nil
+                       }
+                       return rrSet
+               }
+               recs := getRecs()
+               // assemble response
+               resp := message.NewNamestoreRecordLookupRespMsg(m.ID, 
m.ZoneKey, string(m.Label))
+               if recs != nil {
+                       resp.AddRecords(recs)
+                       resp.Found = int16(enums.RC_YES)
+               } else {
+                       resp.Found = int16(enums.RC_NO)
+               }
+               if !sendResponse(ctx, "namestore"+label, resp, back) {
+                       return false
+               }
+
+       // lookup records based on query hash
+       case *message.NamestoreZoneToNameMsg:
+               ec := enums.EC_NONE
+               var label string
+
+               // get resource records
+               getRecs := func(hsh *crypto.HashCode) *blocks.RecordSet {
+                       lbl, err := s.zm.zdb.GetLabelByKeyHash(hsh)
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[namestore%s] 
label hash lookup: %s", label, err.Error())
+                               ec = enums.EC_NAMESTORE_LOOKUP_ERROR
+                               return nil
+                       }
+                       label = lbl.Name
+                       rrSet, _, err := s.zm.GetRecordSet(lbl.ID, 
enums.GNS_FILTER_NONE)
+                       if err != nil {
+                               logger.Printf(logger.ERROR, "[namestore%s] 
records: %s", label, err.Error())
+                               ec = enums.EC_NAMESTORE_LOOKUP_ERROR
+                               return nil
+                       }
+                       if rrSet.Count == 0 {
+                               ec = enums.EC_NAMESTORE_NO_RESULTS
+                       }
+                       return rrSet
+               }
+               recs := getRecs(crypto.Hash(m.ZonePublic.KeyData))
+
+               resp := message.NewNamestoreZoneToNameRespMsg(m.ID, m.ZoneKey, 
label, ec)
+               if recs != nil {
+                       resp.AddRecords(recs)
+               }
+               if !sendResponse(ctx, "namestore"+label, resp, back) {
+                       return false
+               }
+       }
+       return true
+}
diff --git a/src/gnunet/service/zonemaster/zonemaster.go 
b/src/gnunet/service/zonemaster/zonemaster.go
index 4703d2c..01204d4 100644
--- a/src/gnunet/service/zonemaster/zonemaster.go
+++ b/src/gnunet/service/zonemaster/zonemaster.go
@@ -26,6 +26,7 @@ import (
        "gnunet/service/dht/blocks"
        "gnunet/service/store"
        "gnunet/util"
+       "plugin"
        "time"
 
        "github.com/bfix/gospel/logger"
@@ -41,16 +42,20 @@ import (
 type ZoneMaster struct {
        Module
 
-       zdb       *store.ZoneDB     // ZoneDB connection
-       namestore *NamestoreService // namestore subservice
-       identity  *IdentityService  // identity subservice
+       zdb       *store.ZoneDB            // ZoneDB connection
+       plugins   []Plugin                 // list of loaded plugins
+       hdlrs     map[enums.GNSType]Plugin // maps record types to handling 
plugin
+       namestore *NamestoreService        // namestore subservice
+       identity  *IdentityService         // identity subservice
 }
 
 // NewService initializes a new zone master service.
-func NewService(ctx context.Context, c *core.Core) *ZoneMaster {
+func NewService(ctx context.Context, c *core.Core, plugins []string) 
*ZoneMaster {
        mod := NewModule(ctx, c)
        srv := &ZoneMaster{
-               Module: *mod,
+               Module:  *mod,
+               plugins: make([]Plugin, 0),
+               hdlrs:   make(map[enums.GNSType]Plugin),
        }
 
        // set external function references (external services)
@@ -61,6 +66,38 @@ func NewService(ctx context.Context, c *core.Core) 
*ZoneMaster {
        srv.namestore = NewNamestoreService(srv)
        srv.identity = NewIdentityService(srv)
 
+       // load all plugins
+       for _, pn := range plugins {
+               // get handle to plugin
+               plugin, err := plugin.Open(pn)
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] %v", err)
+                       continue
+               }
+               // get plugin instance
+               sym, err := plugin.Lookup("Plugin")
+               if err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] can't lookup 
plugin instance: %v", err)
+                       continue
+               }
+               inst, ok := sym.(Plugin)
+               if !ok {
+                       logger.Println(logger.ERROR, "[zonemaster] can't cast 
plugin instance")
+                       continue
+               }
+               logger.Printf(logger.INFO, "[zonemaster] plugin '%s' loaded.", 
inst.Name())
+
+               // register Utility function with plugin
+               inst.SetUtility(ZoneMasterUtility)
+
+               // add plugin to resource record type handler
+               srv.plugins = append(srv.plugins, inst)
+               for _, t := range inst.CanHandle() {
+                       gt := enums.GNSType(t)
+                       srv.hdlrs[gt] = inst
+                       logger.Printf(logger.INFO, "[zonemaster] Plugin handles 
type %s (%d)", gt, t)
+               }
+       }
        return srv
 }
 
@@ -84,7 +121,6 @@ func (zm *ZoneMaster) Run(ctx context.Context) {
        // publish on start-up
        if err = zm.Publish(ctx); err != nil {
                logger.Printf(logger.ERROR, "[zonemaster] initial publish 
failed: %s", err.Error())
-               return
        }
 
        // periodically publish GNS blocks to the DHT
@@ -106,6 +142,79 @@ loop:
 
 // OnChange is called if a zone or record has changed or was inserted
 func (zm *ZoneMaster) OnChange(table string, id int64, mode int) {
+       // no action on delete
+       if mode == ChangeDelete {
+               return
+       }
+       // handle new and changed entries
+       var (
+               zone  *store.Zone
+               label *store.Label
+               rec   *store.Record
+               err   error
+       )
+       ctx := context.Background()
+       switch table {
+
+       // zone changed
+       case "zones":
+               // a new zone can't have labels...
+               if mode == ChangeNew {
+                       return
+               }
+               // get zone
+               if zone, err = zm.zdb.GetZone(id); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(zone) failed: %s", err.Error())
+                       return
+               }
+               // collect labels for zone
+               var labels []*store.Label
+               if labels, err = zm.zdb.GetLabels("zid=%d", id); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(zone) failed: %s", err.Error())
+                       return
+               }
+               for _, l := range labels {
+                       // publish label
+                       if err = zm.PublishZoneLabel(ctx, zone, l); err != nil {
+                               logger.Printf(logger.ERROR, "[zonemaster] 
OnChange (zone) failed: %s", err.Error())
+                               return
+                       }
+               }
+
+       // record changed
+       case "records":
+               // get record
+               if rec, err = zm.zdb.GetRecord(id); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(record) failed: %s", err.Error())
+                       return
+               }
+               // intended fall through...
+               id = rec.Label
+               mode = ChangeUpdate
+               fallthrough
+
+       // label changed
+       case "labels":
+               // a new label can't have records...
+               if mode == ChangeNew {
+                       return
+               }
+               // get label
+               if label, err = zm.zdb.GetLabel(id); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(label) failed: %s", err.Error())
+                       return
+               }
+               // get zone
+               if zone, err = zm.zdb.GetZone(id); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(label) failed: %s", err.Error())
+                       return
+               }
+               // publish label
+               if err = zm.PublishZoneLabel(ctx, zone, label); err != nil {
+                       logger.Printf(logger.ERROR, "[zonemaster] OnChange 
(label) failed: %s", err.Error())
+                       return
+               }
+       }
 }
 
 // Publish all zone labels to the DHT
@@ -137,7 +246,7 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx context.Context, 
zone *store.Zone, la
        logger.Printf(logger.INFO, "[zonemaster] Publishing label '%s' of zone 
%s", label.Name, zk.ID())
 
        // collect all records for label
-       rrSet, expire, err := zm.getRecords(zk, label.ID)
+       rrSet, expire, err := zm.GetRecordSet(label.ID, enums.GNS_FILTER_NONE)
        if err != nil {
                return err
        }
@@ -145,6 +254,15 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx 
context.Context, zone *store.Zone, la
                logger.Println(logger.INFO, "[zonemaster] No resource records 
-- skipped")
                return nil
        }
+       // post-process records for publication
+       for _, rec := range rrSet.Records {
+               // handle relative expiration
+               if rec.Flags&enums.GNS_FLAG_RELATIVE_EXPIRATION != 0 {
+                       rec.Flags &^= enums.GNS_FLAG_RELATIVE_EXPIRATION
+                       ttl := time.Duration(rec.Expire.Val) * time.Microsecond
+                       rec.Expire = util.AbsoluteTimeNow().Add(ttl)
+               }
+       }
 
        // assemble GNS query (common for DHT and Namecache)
        query := blocks.NewGNSQuery(zk, label.Name)
@@ -173,7 +291,7 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx context.Context, 
zone *store.Zone, la
        // build block for DHT
        blkDHT, _ := blocks.NewGNSBlock().(*blocks.GNSBlock)
        blkDHT.Body.Expire = expire
-       blkDHT.Body.Data, err = zk.Encrypt(rrSet.Bytes(), label.Name, expire)
+       blkDHT.Body.Data, err = zk.Encrypt(rrSet.RDATA(), label.Name, expire)
        if err != nil {
                return err
        }
@@ -203,7 +321,7 @@ func (zm *ZoneMaster) PublishZoneLabel(ctx context.Context, 
zone *store.Zone, la
        // build block for Namecache
        blkNC, _ := blocks.NewGNSBlock().(*blocks.GNSBlock)
        blkNC.Body.Expire = expire
-       blkNC.Body.Data = rrSet.Bytes()
+       blkNC.Body.Data = rrSet.RDATA()
        // sign block
        if dzk, _, err = zone.Key.Derive(label.Name, "gns"); err != nil {
                return err
diff --git a/src/gnunet/sync_with_gana.sh b/src/gnunet/sync_with_gana.sh
new file mode 100755
index 0000000..2242dc7
--- /dev/null
+++ b/src/gnunet/sync_with_gana.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+
+changed=0
+
+function update() {
+    rc=$(diff "$1" "$2")
+    if [ -n "$rc" ]; then
+        cp "$2" "$1"
+        echo "Updated registry file '$1' from '$2'"
+        changed=1
+    else
+        echo "Up-to-date registry file '$1'"
+    fi
+}
+
+# Synchronize GANA definitions
+
+REPO=../../../gana
+
+pushd $REPO
+git pull
+popd
+
+update enums/gnunet-signature.rec $REPO/gnunet-signatures/registry.rec
+update enums/gnunet-dht.rec $REPO/gnunet-dht-block-types/registry.rec
+update enums/gnunet-gns.rec $REPO/gnu-name-system-record-types/registry.rec
+update enums/gnunet-error-codes.rec $REPO/gnunet-error-codes/registry.rec
+update enums/gnunet-gns-flags.rec 
$REPO/gnu-name-system-record-flags/registry.rec
+
+if [ $changed -eq 1 ]; then
+    go generate ./...
+fi

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]