The use of "istruc" is to initialize an instance of a struc.
The so-called local label ".byte" is "valid" from the previous non-local label ("str", in this case) to the next non-local label (there's a mechanism, "..@label" which is neither local nor non-local - it won't break the "scope" - I don't think it will help here)
Outside of this "scope", Nasm's local labels (unlike "true" local labels) can be referred to by its "full name" - "str.byte" in this case.
Within the "scope" begun by "label:", there is no local label ".byte", so "label.byte" won't work. Outside of the "scope", begun by "str" and ended by "label", the only name recognized is "str.byte" - and it's value is the offset of ".byte" from the beginning of the struc. So you need:
mov al, [label + str.byte]
Note that the C syntax "strucname.membername" does something completely different (the full address) from "strucname.localname", which is merely the "full name" of the member (and evaluates to zero, in this case) - you'll have to add the offset of the beginning of the struc to get the "full address". This applies even if you don't use local labels as the names of your members - but it looks less like the C syntax, which may reduce confusion.
If you observe that this makes using local labels as element names less useful than it might be, I'd agree, but that's "the way it works", and I don't think it's fixable without a complete rework of the local label mechanism... which I don't see happening.
This all may seem somewhat strange - especially if you're expecting C-like syntax - but it's a pretty "workable" mechanism, I find, once you get used to it (YMMV).
As to working "without macroses", "struc" *is* a macro, so good luck with that!
If you can think of how this might be more clearly documented - help us out!
Best,
Frank